From 2e0d7a1bb1a378738c9a0caea85b8a120817dc3b Mon Sep 17 00:00:00 2001 From: Lean Date: Sun, 4 Oct 2026 14:28:30 +0200 Subject: [PATCH 01/10] Extract shared functions --- Scripts/Include/AhkProcess.ahk | 122 ++++++++++++++++++++++++ Scripts/Include/Monitor.ahk | 122 +----------------------- Scripts/Include/MonitorOnce.ahk | 160 +------------------------------- 3 files changed, 124 insertions(+), 280 deletions(-) create mode 100644 Scripts/Include/AhkProcess.ahk diff --git a/Scripts/Include/AhkProcess.ahk b/Scripts/Include/AhkProcess.ahk new file mode 100644 index 00000000..8e455c82 --- /dev/null +++ b/Scripts/Include/AhkProcess.ahk @@ -0,0 +1,122 @@ +; Helpers for finding and terminating AutoHotkey script processes by script name + +killAHK(scriptName := "") +{ + killed := 0 + + if(scriptName != "") { + DetectHiddenWindows, On + killedPIDs := {} + killed += killAHKWindowsByClass(scriptName, "AutoHotkey", killedPIDs) + killed += killAHKWindowsByClass(scriptName, "#32770", killedPIDs) + killed += killAHKWindowsByClass(scriptName, "ConsoleWindowClass", killedPIDs) + killed += killAHKProcessesByCommandLine(scriptName, killedPIDs) + } + + return killed +} + +checkAHK(scriptName := "") +{ + cnt := 0 + + if(scriptName != "") { + DetectHiddenWindows, On + seenPIDs := {} + cnt += countAHKWindowsByClass(scriptName, "AutoHotkey", seenPIDs) + cnt += countAHKWindowsByClass(scriptName, "#32770", seenPIDs) + cnt += countAHKWindowsByClass(scriptName, "ConsoleWindowClass", seenPIDs) + cnt += countAHKProcessesByCommandLine(scriptName, seenPIDs) + } + + return cnt +} + +killAHKWindowsByClass(scriptName, winClass, killedPIDs) +{ + killed := 0 + WinGet, IDList, List, ahk_class %winClass% + Loop %IDList% + { + ID:=IDList%A_Index% + WinGetTitle, ATitle, ahk_id %ID% + if (isAHKScriptWindowTitle(ATitle, scriptName)) { + ; Use Process Close (TerminateProcess) instead of WinKill (WM_CLOSE) + ; to guarantee the process dies even if blocked on ADB/Sleep. + WinGet, ahkPID, PID, ahk_id %ID% + if (ahkPID && !killedPIDs.HasKey(ahkPID)) { + Process, Close, %ahkPID% + killedPIDs[ahkPID] := true + killed := killed + 1 + } + } + } + + return killed +} + +countAHKWindowsByClass(scriptName, winClass, seenPIDs) +{ + cnt := 0 + WinGet, IDList, List, ahk_class %winClass% + Loop %IDList% + { + ID:=IDList%A_Index% + WinGetTitle, ATitle, ahk_id %ID% + if (isAHKScriptWindowTitle(ATitle, scriptName)) { + WinGet, ahkPID, PID, ahk_id %ID% + if (ahkPID && !seenPIDs.HasKey(ahkPID)) { + seenPIDs[ahkPID] := true + cnt := cnt + 1 + } + } + } + + return cnt +} + +killAHKProcessesByCommandLine(scriptName, killedPIDs) +{ + killed := 0 + scriptNeedle := "\" . scriptName + + for process in ComObjGet("winmgmts:").ExecQuery("Select ProcessId, Name, CommandLine from Win32_Process Where Name like 'AutoHotkey%'") + { + commandLine := process.CommandLine + if(commandLine != "" && InStr(commandLine, scriptNeedle)) { + ahkPID := process.ProcessId + if (ahkPID && !killedPIDs.HasKey(ahkPID)) { + Process, Close, %ahkPID% + killedPIDs[ahkPID] := true + killed := killed + 1 + } + } + } + + return killed +} + +countAHKProcessesByCommandLine(scriptName, seenPIDs) +{ + cnt := 0 + scriptNeedle := "\" . scriptName + + for process in ComObjGet("winmgmts:").ExecQuery("Select ProcessId, Name, CommandLine from Win32_Process Where Name like 'AutoHotkey%'") + { + commandLine := process.CommandLine + if(commandLine != "" && InStr(commandLine, scriptNeedle)) { + ahkPID := process.ProcessId + if (ahkPID && !seenPIDs.HasKey(ahkPID)) { + seenPIDs[ahkPID] := true + cnt := cnt + 1 + } + } + } + + return cnt +} + +isAHKScriptWindowTitle(ATitle, scriptName) +{ + return (InStr(ATitle, "\" . scriptName) || ATitle = scriptName) +} diff --git a/Scripts/Include/Monitor.ahk b/Scripts/Include/Monitor.ahk index 856c016e..51a29fd1 100644 --- a/Scripts/Include/Monitor.ahk +++ b/Scripts/Include/Monitor.ahk @@ -4,6 +4,7 @@ #Include %A_ScriptDir%\GitManager.ahk #Include %A_ScriptDir%\MumuHelper.ahk #Include %A_ScriptDir%\Utils.ahk +#Include %A_ScriptDir%\AhkProcess.ahk #SingleInstance, force CoordMode, Mouse, Screen @@ -418,125 +419,4 @@ GetProcessMemory(hProcess) { return 0 } -killAHK(scriptName := "") -{ - killed := 0 - - if(scriptName != "") { - DetectHiddenWindows, On - killedPIDs := {} - killed += killAHKWindowsByClass(scriptName, "AutoHotkey", killedPIDs) - killed += killAHKWindowsByClass(scriptName, "#32770", killedPIDs) - killed += killAHKWindowsByClass(scriptName, "ConsoleWindowClass", killedPIDs) - killed += killAHKProcessesByCommandLine(scriptName, killedPIDs) - } - - return killed -} - -checkAHK(scriptName := "") -{ - cnt := 0 - - if(scriptName != "") { - DetectHiddenWindows, On - seenPIDs := {} - cnt += countAHKWindowsByClass(scriptName, "AutoHotkey", seenPIDs) - cnt += countAHKWindowsByClass(scriptName, "#32770", seenPIDs) - cnt += countAHKWindowsByClass(scriptName, "ConsoleWindowClass", seenPIDs) - cnt += countAHKProcessesByCommandLine(scriptName, seenPIDs) - } - - return cnt -} - -killAHKWindowsByClass(scriptName, winClass, killedPIDs) -{ - killed := 0 - WinGet, IDList, List, ahk_class %winClass% - Loop %IDList% - { - ID:=IDList%A_Index% - WinGetTitle, ATitle, ahk_id %ID% - if (isAHKScriptWindowTitle(ATitle, scriptName)) { - ; Use Process Close (TerminateProcess) instead of WinKill (WM_CLOSE) - ; to guarantee the process dies even if blocked on ADB/Sleep. - WinGet, ahkPID, PID, ahk_id %ID% - if (ahkPID && !killedPIDs.HasKey(ahkPID)) { - Process, Close, %ahkPID% - killedPIDs[ahkPID] := true - killed := killed + 1 - } - } - } - - return killed -} - -countAHKWindowsByClass(scriptName, winClass, seenPIDs) -{ - cnt := 0 - WinGet, IDList, List, ahk_class %winClass% - Loop %IDList% - { - ID:=IDList%A_Index% - WinGetTitle, ATitle, ahk_id %ID% - if (isAHKScriptWindowTitle(ATitle, scriptName)) { - WinGet, ahkPID, PID, ahk_id %ID% - if (ahkPID && !seenPIDs.HasKey(ahkPID)) { - seenPIDs[ahkPID] := true - cnt := cnt + 1 - } - } - } - - return cnt -} - -killAHKProcessesByCommandLine(scriptName, killedPIDs) -{ - killed := 0 - scriptNeedle := "\" . scriptName - - for process in ComObjGet("winmgmts:").ExecQuery("Select ProcessId, Name, CommandLine from Win32_Process Where Name like 'AutoHotkey%'") - { - commandLine := process.CommandLine - if(commandLine != "" && InStr(commandLine, scriptNeedle)) { - ahkPID := process.ProcessId - if (ahkPID && !killedPIDs.HasKey(ahkPID)) { - Process, Close, %ahkPID% - killedPIDs[ahkPID] := true - killed := killed + 1 - } - } - } - - return killed -} - -countAHKProcessesByCommandLine(scriptName, seenPIDs) -{ - cnt := 0 - scriptNeedle := "\" . scriptName - - for process in ComObjGet("winmgmts:").ExecQuery("Select ProcessId, Name, CommandLine from Win32_Process Where Name like 'AutoHotkey%'") - { - commandLine := process.CommandLine - if(commandLine != "" && InStr(commandLine, scriptNeedle)) { - ahkPID := process.ProcessId - if (ahkPID && !seenPIDs.HasKey(ahkPID)) { - seenPIDs[ahkPID] := true - cnt := cnt + 1 - } - } - } - - return cnt -} - -isAHKScriptWindowTitle(ATitle, scriptName) -{ - return (InStr(ATitle, "\" . scriptName) || ATitle = scriptName) -} - ~+F7::ExitApp diff --git a/Scripts/Include/MonitorOnce.ahk b/Scripts/Include/MonitorOnce.ahk index dda99bd4..b5bdd11d 100644 --- a/Scripts/Include/MonitorOnce.ahk +++ b/Scripts/Include/MonitorOnce.ahk @@ -2,6 +2,7 @@ #Include %A_ScriptDir%\Profiler.ahk #Include %A_ScriptDir%\Logging.ahk #Include %A_ScriptDir%\Utils.ahk +#Include %A_ScriptDir%\AhkProcess.ahk #SingleInstance, force CoordMode, Mouse, Screen @@ -71,163 +72,4 @@ Loop %Instances% { ExitApp -killAHK(scriptName := "") -{ - killed := 0 - - if(scriptName != "") { - DetectHiddenWindows, On - killedPIDs := {} - killed += killAHKWindowsByClass(scriptName, "AutoHotkey", killedPIDs) - killed += killAHKWindowsByClass(scriptName, "#32770", killedPIDs) - killed += killAHKWindowsByClass(scriptName, "ConsoleWindowClass", killedPIDs) - killed += killAHKProcessesByCommandLine(scriptName, killedPIDs) - } - - return killed -} - -checkAHK(scriptName := "") -{ - cnt := 0 - - if(scriptName != "") { - DetectHiddenWindows, On - seenPIDs := {} - cnt += countAHKWindowsByClass(scriptName, "AutoHotkey", seenPIDs) - cnt += countAHKWindowsByClass(scriptName, "#32770", seenPIDs) - cnt += countAHKWindowsByClass(scriptName, "ConsoleWindowClass", seenPIDs) - cnt += countAHKProcessesByCommandLine(scriptName, seenPIDs) - } - - return cnt -} - -killAHKWindowsByClass(scriptName, winClass, killedPIDs) -{ - killed := 0 - WinGet, IDList, List, ahk_class %winClass% - Loop %IDList% - { - ID:=IDList%A_Index% - WinGetTitle, ATitle, ahk_id %ID% - if (isAHKScriptWindowTitle(ATitle, scriptName)) { - WinGet, ahkPID, PID, ahk_id %ID% - if (ahkPID && !killedPIDs.HasKey(ahkPID)) { - Process, Close, %ahkPID% - killedPIDs[ahkPID] := true - killed := killed + 1 - } - } - } - - return killed -} - -countAHKWindowsByClass(scriptName, winClass, seenPIDs) -{ - cnt := 0 - WinGet, IDList, List, ahk_class %winClass% - Loop %IDList% - { - ID:=IDList%A_Index% - WinGetTitle, ATitle, ahk_id %ID% - if (isAHKScriptWindowTitle(ATitle, scriptName)) { - WinGet, ahkPID, PID, ahk_id %ID% - if (ahkPID && !seenPIDs.HasKey(ahkPID)) { - seenPIDs[ahkPID] := true - cnt := cnt + 1 - } - } - } - - return cnt -} - -killAHKProcessesByCommandLine(scriptName, killedPIDs) -{ - killed := 0 - scriptNeedle := "\" . scriptName - - for process in ComObjGet("winmgmts:").ExecQuery("Select ProcessId, Name, CommandLine from Win32_Process Where Name like 'AutoHotkey%'") - { - commandLine := process.CommandLine - if(commandLine != "" && InStr(commandLine, scriptNeedle)) { - ahkPID := process.ProcessId - if (ahkPID && !killedPIDs.HasKey(ahkPID)) { - Process, Close, %ahkPID% - killedPIDs[ahkPID] := true - killed := killed + 1 - } - } - } - - return killed -} - -countAHKProcessesByCommandLine(scriptName, seenPIDs) -{ - cnt := 0 - scriptNeedle := "\" . scriptName - - for process in ComObjGet("winmgmts:").ExecQuery("Select ProcessId, Name, CommandLine from Win32_Process Where Name like 'AutoHotkey%'") - { - commandLine := process.CommandLine - if(commandLine != "" && InStr(commandLine, scriptNeedle)) { - ahkPID := process.ProcessId - if (ahkPID && !seenPIDs.HasKey(ahkPID)) { - seenPIDs[ahkPID] := true - cnt := cnt + 1 - } - } - } - - return cnt -} - -isAHKScriptWindowTitle(ATitle, scriptName) -{ - return (InStr(ATitle, "\" . scriptName) || ATitle = scriptName) -} - -killInstance(instanceNum := "") -{ - killed := 0 - - pID := checkInstance(instanceNum) - if pID { - Process, Close, %pID% - killed := killed + 1 - } - - return killed -} - -checkInstance(instanceNum := "") -{ - ret := WinExist(instanceNum) - if(ret) - { - WinGet, temp_pid, PID, ahk_id %ret% - return temp_pid - } - - return "" -} - -launchInstance(instanceNum := "") -{ - global mumuFolder - - if(instanceNum != "") { - mumuNum := getMumuInstanceNum(instanceNum, mumuFolder) - if(mumuNum != "") { - mumuExe := mumuFolder . "\shell\MuMuPlayer.exe" - if !FileExist(mumuExe) - mumuExe := mumuFolder . "\nx_main\MuMuNxMain.exe" - Run_(mumuExe, "-v " . mumuNum) - } - } -} - ~+F7::ExitApp From 6eae6e1e2164c814d22768dfba98d6799ce904ad Mon Sep 17 00:00:00 2001 From: Lean Date: Sun, 4 Oct 2026 14:41:06 +0200 Subject: [PATCH 02/10] Move more common functions --- Scripts/1.ahk | 103 +---------------------- Scripts/Include/BotCommon.ahk | 25 ++++++ Scripts/Include/BoundingBox.ahk | 121 ++++++++++++++++++++++++++ Scripts/Main.ahk | 145 +------------------------------- 4 files changed, 148 insertions(+), 246 deletions(-) create mode 100644 Scripts/Include/BotCommon.ahk create mode 100644 Scripts/Include/BoundingBox.ahk diff --git a/Scripts/1.ahk b/Scripts/1.ahk index 28a222a2..8159bfc5 100644 --- a/Scripts/1.ahk +++ b/Scripts/1.ahk @@ -7,34 +7,12 @@ CoordMode, Pixel, Screen #NoEnv #Include %A_ScriptDir%\Include\ -#Include Config.ahk -#Include Session.ahk -#Include Data.ahk -#Include ExtraConfig.ahk -#Include Profiler.ahk -#Include Gdip_All.ahk -#Include Gdip_Imagesearch.ahk +#Include BotCommon.ahk pToken := Gdip_Startup() -#Include MumuHelper.ahk -#Include Utils.ahk -#Include Logging.ahk -#Include ADB.ahk -#Include OCR.ahk -#Include Gdip_Extra.ahk -#Include AccountMetadata.ahk -#Include Database.ahk -#Include Wishlist.ahk -#Include CardNames.ahk -#Include CardDetection.ahk -#Include AccountManager.ahk -#Include FriendManager.ahk ;#Include %A_ScriptDir%\Include\Recorder.ahk #Include Packs.ahk -#Include Error.ahk -#Include Coords.ahk #Include RarityBorder.ahk #Include SpecialEvent.ahk -#Include Crinity_UnofficialPatch.ahk #Include HourglassSpend.ahk InitializeHiddenConsole() @@ -3381,32 +3359,6 @@ TogglebboxNpause() { session.set("dbg_bboxNpause", !session.get("dbg_bboxNpause")) } -bboxDraw(X1, Y1, X2, Y2, color) { - global session - - WinGetPos, xwin, ywin, Width, Height, % "ahk_id " . getMuMuHwnd(session.get("winTitle")) - BoxWidth := X2-X1 - BoxHeight := Y2-Y1 - ; Create a GUI - guiSuffix := session.get("winTitle") - Gui, BoundingBox%guiSuffix%:+AlwaysOnTop +ToolWindow -Caption +E0x20 - Gui, BoundingBox%guiSuffix%:Color, 123456 - Gui, BoundingBox%guiSuffix%:+LastFound ; Make the GUI window the last found window for use by the line below. (straght from documentation) - WinSet, TransColor, 123456 ; Makes that specific color transparent in the gui - - ; Create the borders and show - Gui, BoundingBox%guiSuffix%:Add, Progress, x0 y0 w%BoxWidth% h2 %color% - Gui, BoundingBox%guiSuffix%:Add, Progress, x0 y0 w2 h%BoxHeight% %color% - Gui, BoundingBox%guiSuffix%:Add, Progress, x%BoxWidth% y0 w2 h%BoxHeight% %color% - Gui, BoundingBox%guiSuffix%:Add, Progress, x0 y%BoxHeight% w%BoxWidth% h2 %color% - - xshow := X1+xwin - yshow := Y1+ywin - Gui, BoundingBox%guiSuffix%:Show, x%xshow% y%yshow% NoActivate - Sleep, 100 - -} - bboxDraw2(X1, Y1, X2, Y2, color) { global session @@ -3486,59 +3438,6 @@ bboxAndPause_swipe(params, doPause := False) { Gui, BoundingBox%guiSuffix%:Destroy } -adbClick_wbb(X,Y) { - prof := Prof_Scope(A_ThisFunc) - global session - - if(session.get("dbg_bbox")) - bboxAndPause_click(X, Y, session.get("dbg_bboxNpause")) - adbClick(X,Y) -} - -bboxAndPause_click(X, Y, doPause := False) { - global session - - guiSuffix := session.get("winTitle") - CreateStatusMessage("Clicking X " . X . " Y " . Y,,,, false) - - color := "BackgroundBlue" - - bboxDraw(X-5, Y-5, X+5, Y+5, color) - - if (doPause) { - Pause - } - - if GetKeyState("F4", "P") { - Pause - } - Gui, BoundingBox%guiSuffix%:Destroy -} - -bboxAndPause_immage(X1, Y1, X2, Y2, pNeedleObj, vret := False, doPause := False) { - global session - - guiSuffix := session.get("winTitle") - CreateStatusMessage("Searching " . pNeedleObj.Name . " returns " . vret,,,, false) - - if(vret>0) { - color := "BackgroundGreen" - } else { - color := "BackgroundRed" - } - - bboxDraw(X1, Y1, X2, Y2, color) - - if (doPause && vret) { - Pause - } - - if GetKeyState("F4", "P") { - Pause - } - Gui, BoundingBox%guiSuffix%:Destroy -} - Gdip_ImageSearch_wbb(pBitmapHaystack,pNeedle,ByRef OutputList="" ,OuterX1=0,OuterY1=0,OuterX2=0,OuterY2=0,Variation=0,Trans="" ,SearchDirection=1,Instances=1,LineDelim="`n",CoordDelim=",") { diff --git a/Scripts/Include/BotCommon.ahk b/Scripts/Include/BotCommon.ahk new file mode 100644 index 00000000..f76b4e0b --- /dev/null +++ b/Scripts/Include/BotCommon.ahk @@ -0,0 +1,25 @@ +; Includes shared by the bot scripts (1.ahk, Main.ahk). Order matters for files that run code at load time. +#Include %A_LineFile%\..\Config.ahk +#Include %A_LineFile%\..\Session.ahk +#Include %A_LineFile%\..\Data.ahk +#Include %A_LineFile%\..\ExtraConfig.ahk +#Include %A_LineFile%\..\Profiler.ahk +#Include %A_LineFile%\..\Gdip_All.ahk +#Include %A_LineFile%\..\Gdip_Imagesearch.ahk +#Include %A_LineFile%\..\MumuHelper.ahk +#Include %A_LineFile%\..\Utils.ahk +#Include %A_LineFile%\..\Logging.ahk +#Include %A_LineFile%\..\ADB.ahk +#Include %A_LineFile%\..\OCR.ahk +#Include %A_LineFile%\..\Gdip_Extra.ahk +#Include %A_LineFile%\..\AccountMetadata.ahk +#Include %A_LineFile%\..\Database.ahk +#Include %A_LineFile%\..\Wishlist.ahk +#Include %A_LineFile%\..\CardNames.ahk +#Include %A_LineFile%\..\CardDetection.ahk +#Include %A_LineFile%\..\AccountManager.ahk +#Include %A_LineFile%\..\FriendManager.ahk +#Include %A_LineFile%\..\Error.ahk +#Include %A_LineFile%\..\Coords.ahk +#Include %A_LineFile%\..\Crinity_UnofficialPatch.ahk +#Include %A_LineFile%\..\BoundingBox.ahk diff --git a/Scripts/Include/BoundingBox.ahk b/Scripts/Include/BoundingBox.ahk new file mode 100644 index 00000000..44b2c3bc --- /dev/null +++ b/Scripts/Include/BoundingBox.ahk @@ -0,0 +1,121 @@ +; Debug helpers that draw bounding boxes over the MuMu window + +; Draws a rectangular bounding box overlay on the screen for debugging. +bboxDraw(X1, Y1, X2, Y2, color) { + ; ------------------------------------------------------------------------------ + ; Parameters: + ; X1 (Int) - Top-left X coordinate + ; Y1 (Int) - Top-left Y coordinate + ; X2 (Int) - Bottom-right X coordinate + ; Y2 (Int) - Bottom-right Y coordinate + ; color (Str) - Color name for the box borders + ; + ; Creates a transparent GUI overlay with colored borders to show a region. + ; ------------------------------------------------------------------------------ + global session + + WinGetPos, xwin, ywin, Width, Height, % "ahk_id " . getMuMuHwnd(session.get("winTitle")) + BoxWidth := X2-X1 + BoxHeight := Y2-Y1 + ; Create a GUI + guiSuffix := session.get("winTitle") + Gui, BoundingBox%guiSuffix%:+AlwaysOnTop +ToolWindow -Caption +E0x20 + Gui, BoundingBox%guiSuffix%:Color, 123456 + Gui, BoundingBox%guiSuffix%:+LastFound ; Make the GUI window the last found window for use by the line below. (straght from documentation) + WinSet, TransColor, 123456 ; Makes that specific color transparent in the gui + + ; Create the borders and show + Gui, BoundingBox%guiSuffix%:Add, Progress, x0 y0 w%BoxWidth% h2 %color% + Gui, BoundingBox%guiSuffix%:Add, Progress, x0 y0 w2 h%BoxHeight% %color% + Gui, BoundingBox%guiSuffix%:Add, Progress, x%BoxWidth% y0 w2 h%BoxHeight% %color% + Gui, BoundingBox%guiSuffix%:Add, Progress, x0 y%BoxHeight% w%BoxWidth% h2 %color% + + xshow := X1+xwin + yshow := Y1+ywin + Gui, BoundingBox%guiSuffix%:Show, x%xshow% y%yshow% NoActivate + Sleep, 100 + +} + +; Displays a bounding box at click location for debugging purposes. +bboxAndPause_click(X, Y, doPause := False) { + ; ------------------------------------------------------------------------------ + ; Parameters: + ; X (Int) - X-coordinate center of box + ; Y (Int) - Y-coordinate center of box + ; doPause (Bool) - Whether to pause execution after displaying box + ; + ; Shows a small box around the click point and optionally pauses for debugging. + ; ------------------------------------------------------------------------------ + global session + + guiSuffix := session.get("winTitle") + CreateStatusMessage("Clicking X " . X . " Y " . Y,,,, false) + + color := "BackgroundBlue" + + bboxDraw(X-5, Y-5, X+5, Y+5, color) + + if (doPause) { + Pause + } + + if GetKeyState("F4", "P") { + Pause + } + Gui, BoundingBox%guiSuffix%:Destroy +} + +; Displays a bounding box for image search debugging. +bboxAndPause_immage(X1, Y1, X2, Y2, pNeedleObj, vret := False, doPause := False) { + ; ------------------------------------------------------------------------------ + ; Parameters: + ; X1 (Int) - Top-left X coordinate of search region + ; Y1 (Int) - Top-left Y coordinate of search region + ; X2 (Int) - Bottom-right X coordinate of search region + ; Y2 (Int) - Bottom-right Y coordinate of search region + ; pNeedleObj (Obj) - Needle object containing Name property + ; vret (Mixed) - Return value from image search (for color coding) + ; doPause (Bool) - Whether to pause if image found + ; + ; Shows green box if image found, red if not found. + ; ------------------------------------------------------------------------------ + global session + + guiSuffix := session.get("winTitle") + CreateStatusMessage("Searching " . pNeedleObj.Name . " returns " . vret,,,, false) + + if(vret>0) { + color := "BackgroundGreen" + } else { + color := "BackgroundRed" + } + + bboxDraw(X1, Y1, X2, Y2, color) + + if (doPause && vret) { + Pause + } + + if GetKeyState("F4", "P") { + Pause + } + Gui, BoundingBox%guiSuffix%:Destroy +} + +; Wrapper for adbClick with optional bounding box debugging display. +adbClick_wbb(X,Y) { + ; ------------------------------------------------------------------------------ + ; Parameters: + ; X (Int) - X-coordinate to click + ; Y (Int) - Y-coordinate to click + ; + ; If dbg_bbox global is enabled, shows a bounding box before clicking. + ; ------------------------------------------------------------------------------ + prof := Prof_Scope(A_ThisFunc) + global session + + if(session.get("dbg_bbox")) + bboxAndPause_click(X, Y, session.get("dbg_bboxNpause")) + adbClick(X,Y) +} diff --git a/Scripts/Main.ahk b/Scripts/Main.ahk index 5f01e0fc..54403bc8 100644 --- a/Scripts/Main.ahk +++ b/Scripts/Main.ahk @@ -6,37 +6,10 @@ SetTitleMatchMode, 3 CoordMode, Pixel, Screen #Include %A_ScriptDir%\Include\ -#Include Config.ahk -#Include Session.ahk -#Include Data.ahk -#Include ExtraConfig.ahk -#Include Profiler.ahk - -#Include Logging.ahk -#Include MumuHelper.ahk -#Include ADB.ahk -#Include Gdip_All.ahk -#Include Gdip_Imagesearch.ahk - -#Include Gdip_Extra.ahk +#Include BotCommon.ahk #Include StringCompare.ahk -#Include OCR.ahk -#Include AccountMetadata.ahk -#Include Database.ahk -#Include Wishlist.ahk -#Include CardNames.ahk -#Include CardDetection.ahk -#Include AccountManager.ahk -#Include FriendManager.ahk #Include Dictionary.ahk -#Include Coords.ahk -#Include Error.ahk - -#Include Utils.ahk -#Include Database.ahk -#Include Crinity_UnofficialPatch.ahk - InitializeHiddenConsole() ; Stubs: These functions are defined in the instance scripts but not needed here. @@ -2196,122 +2169,6 @@ GetTempDirectory() { return tempDir } -; Wrapper for adbClick with optional bounding box debugging display. -adbClick_wbb(X,Y) { - ; ------------------------------------------------------------------------------ - ; Parameters: - ; X (Int) - X-coordinate to click - ; Y (Int) - Y-coordinate to click - ; - ; If dbg_bbox global is enabled, shows a bounding box before clicking. - ; ------------------------------------------------------------------------------ - global session - if(session.get("dbg_bbox")) - bboxAndPause_click(X, Y, session.get("dbg_bboxNpause")) - adbClick(X,Y) -} - -; Displays a bounding box at click location for debugging purposes. -bboxAndPause_click(X, Y, doPause := False) { - ; ------------------------------------------------------------------------------ - ; Parameters: - ; X (Int) - X-coordinate center of box - ; Y (Int) - Y-coordinate center of box - ; doPause (Bool) - Whether to pause execution after displaying box - ; - ; Shows a small box around the click point and optionally pauses for debugging. - ; ------------------------------------------------------------------------------ - global session - CreateStatusMessage("Clicking X " . X . " Y " . Y,,,, false) - - color := "BackgroundBlue" - - bboxDraw(X-5, Y-5, X+5, Y+5, color) - - if (doPause) { - Pause - } - - if GetKeyState("F4", "P") { - Pause - } - guiSuffix := session.get("winTitle") - Gui, BoundingBox%winTguiSuffixitle%:Destroy -} - -; Draws a rectangular bounding box overlay on the screen for debugging. -bboxDraw(X1, Y1, X2, Y2, color) { - ; ------------------------------------------------------------------------------ - ; Parameters: - ; X1 (Int) - Top-left X coordinate - ; Y1 (Int) - Top-left Y coordinate - ; X2 (Int) - Bottom-right X coordinate - ; Y2 (Int) - Bottom-right Y coordinate - ; color (Str) - Color name for the box borders - ; - ; Creates a transparent GUI overlay with colored borders to show a region. - ; ------------------------------------------------------------------------------ - global session - - guiSuffix := session.get("winTitle") - WinGetPos, xwin, ywin, Width, Height, %guiSuffix% - BoxWidth := X2-X1 - BoxHeight := Y2-Y1 - ; Create a GUI - Gui, BoundingBox%guiSuffix%:+AlwaysOnTop +ToolWindow -Caption +E0x20 - Gui, BoundingBox%guiSuffix%:Color, 123456 - Gui, BoundingBox%guiSuffix%:+LastFound ; Make the GUI window the last found window for use by the line below. (straght from documentation) - WinSet, TransColor, 123456 ; Makes that specific color transparent in the gui - - ; Create the borders and show - Gui, BoundingBox%guiSuffix%:Add, Progress, x0 y0 w%BoxWidth% h2 %color% - Gui, BoundingBox%guiSuffix%:Add, Progress, x0 y0 w2 h%BoxHeight% %color% - Gui, BoundingBox%guiSuffix%:Add, Progress, x%BoxWidth% y0 w2 h%BoxHeight% %color% - Gui, BoundingBox%guiSuffix%:Add, Progress, x0 y%BoxHeight% w%BoxWidth% h2 %color% - - xshow := X1+xwin - yshow := Y1+ywin - Gui, BoundingBox%guiSuffix%:Show, x%xshow% y%yshow% NoActivate - Sleep, 100 -} - -; Displays a bounding box for image search debugging. -bboxAndPause_immage(X1, Y1, X2, Y2, pNeedleObj, vret := False, doPause := False) { - ; ------------------------------------------------------------------------------ - ; Parameters: - ; X1 (Int) - Top-left X coordinate of search region - ; Y1 (Int) - Top-left Y coordinate of search region - ; X2 (Int) - Bottom-right X coordinate of search region - ; Y2 (Int) - Bottom-right Y coordinate of search region - ; pNeedleObj (Obj) - Needle object containing Name property - ; vret (Mixed) - Return value from image search (for color coding) - ; doPause (Bool) - Whether to pause if image found - ; - ; Shows green box if image found, red if not found. - ; ------------------------------------------------------------------------------ - global session - - guiSuffix := session.get("winTitle") - CreateStatusMessage("Searching " . pNeedleObj.Name . " returns " . vret,,,, false) - - if(vret>0) { - color := "BackgroundGreen" - } else { - color := "BackgroundRed" - } - - bboxDraw(X1, Y1, X2, Y2, color) - - if (doPause && vret) { - Pause - } - - if GetKeyState("F4", "P") { - Pause - } - Gui, BoundingBox%guiSuffix%:Destroy -} - ; Wrapper for Gdip_ImageSearch with bounding box debugging and title bar offset adjustment. Gdip_ImageSearch_wbb(pBitmapHaystack,pNeedle,ByRef OutputList="" ,OuterX1=0,OuterY1=0,OuterX2=0,OuterY2=0,Variation=0,Trans="" From 4039fae652881e61aa340a0525deed18297a917d Mon Sep 17 00:00:00 2001 From: Lean Date: Sun, 4 Oct 2026 14:59:22 +0200 Subject: [PATCH 03/10] Move account functions --- Scripts/1.ahk | 534 +-------------------------- Scripts/Include/BotAccountMeta.ahk | 284 ++++++++++++++ Scripts/Include/BotAccountRename.ahk | 250 +++++++++++++ 3 files changed, 536 insertions(+), 532 deletions(-) create mode 100644 Scripts/Include/BotAccountMeta.ahk create mode 100644 Scripts/Include/BotAccountRename.ahk diff --git a/Scripts/1.ahk b/Scripts/1.ahk index 8159bfc5..ae06cacb 100644 --- a/Scripts/1.ahk +++ b/Scripts/1.ahk @@ -14,6 +14,8 @@ pToken := Gdip_Startup() #Include RarityBorder.ahk #Include SpecialEvent.ahk #Include HourglassSpend.ahk +#Include BotAccountMeta.ahk +#Include BotAccountRename.ahk InitializeHiddenConsole() @@ -879,49 +881,6 @@ if(DeadCheck = 1 && botConfig.get("deleteMethod") != "Create Bots (13P)") { return -SetBeginnerMissionsDone() { - global session - - session.get("missionDoneList")["beginnerMissionsDone"] := 1 - deviceAccount := GetCurrentDeviceAccountForMetadata() - if (deviceAccount != "") { - AccountMetadata_SetLastLoggedInNow(deviceAccount, session.get("scriptName"), session.get("accountFileName")) - if (session.get("accountFileName") != "") - AccountMetadata_SetFlag(session.get("scriptName"), session.get("accountFileName"), "H", 1) - } - - if (session.get("injectMethod") && session.get("loadedAccount")) { - setMetaData() - if (session.get("accountFileName") != "") - AccountMetadata_SetFlag(session.get("scriptName"), session.get("accountFileName"), "H", 1) - } -} - -SetLastPackPulledNow() { - global session - - deviceAccount := GetCurrentDeviceAccountForMetadata() - if (deviceAccount != "") - AccountMetadata_SetLastPackPulledNow(deviceAccount, session.get("scriptName"), session.get("accountFileName")) -} - -GetCurrentDeviceAccountForMetadata() { - global session - - deviceAccount := session.get("deviceAccount") - if (deviceAccount = "") { - LogDebug("Device account missing in session; reading from XML metadata") - deviceAccount := GetDeviceAccountFromXML() - if (deviceAccount != "") { - LogDebug("Resolved device account from XML for " . session.get("accountFileName")) - session.set("deviceAccount", deviceAccount) - } else { - LogWarn("Could not resolve device account for " . session.get("accountFileName")) - } - } - return deviceAccount -} - HomeAndMission(homeonly := 0, completeSecondMisson=false) { global session @@ -1630,220 +1589,6 @@ menuDeleteStart() { } } -GetAccountCreationDate() { - global session - - if (!session.get("injectMethod") || !session.get("loadedAccount") || session.get("accountFileName") = "") - return false - - LogDebug("GetAccountCreationDate started for " . session.get("accountFileName")) - accountPath := A_ScriptDir . "\..\Accounts\Saved\" . session.get("scriptName") . "\" . session.get("accountFileName") - accountMeta := AccountMetadata_Get(session.get("scriptName"), session.get("accountFileName"), accountPath) - existingCreatedAt := accountMeta["createdAt"] - - LogTrace("Ensuring ptcgpb helper exists before creation-date check", "ADB.txt") - if (!EnsurePTCGPBHelperInstalled()) - return false - - LogTrace("Running ptcgpb earliest for creation-date window", "ADB.txt") - earliestOutput := adbWriteRaw("/data/ptcgp/ptcgpb earliest", true) - earliestOutput := StrReplace(earliestOutput, "`r") - earliestOutput := Trim(earliestOutput, "`n`t ") - if (!RegExMatch(earliestOutput, "(\d{9,12})", earliestMatch)) { - LogWarn("GetAccountCreationDate could not parse earliest output for " . session.get("accountFileName") . ": " . earliestOutput) - return false - } - - earliestUnix := earliestMatch1 + 0 - latestUnix := earliestUnix + 86400 - LogDebug("Creation-date valid window for " . session.get("accountFileName") . ": earliestUnix=" . earliestUnix . ", latestUnix=" . latestUnix) - normalizedExistingCreatedAt := AccountCreationDate_Normalize(existingCreatedAt) - if (normalizedExistingCreatedAt != "") { - existingCreatedAtUnix := AccountCreationDate_ToUnix(normalizedExistingCreatedAt) - if (existingCreatedAtUnix >= earliestUnix && existingCreatedAtUnix <= latestUnix) - { - if (normalizedExistingCreatedAt != existingCreatedAt) { - LogDebug("Normalized account createdAt from Unix timestamp for " . session.get("accountFileName")) - accountMeta["createdAt"] := normalizedExistingCreatedAt - AccountMetadata_SaveAccount(session.get("scriptName"), session.get("accountFileName"), accountMeta) - } - session.set("accountCreatedAt", normalizedExistingCreatedAt) - LogDebug("Using existing account createdAt for " . session.get("accountFileName")) - return true - } - LogDebug("Existing account createdAt outside expected range for " . session.get("accountFileName")) - } - - creationDate := AccountCreationDate_FromUnix(latestUnix) - LogDebug("Defaulting createdAt to latest window bound for " . session.get("accountFileName") . ": " . creationDate) - - if (RegExMatch(session.get("accountFileName"), "^\d+P_(\d{14})_", fileMatch)) { - fileCreationDate := fileMatch1 - fileCreationUnix := AccountCreationDate_ToUnix(fileCreationDate) - if (fileCreationUnix >= earliestUnix && fileCreationUnix <= latestUnix) { - creationDate := fileCreationDate - LogDebug("Using createdAt from filename for " . session.get("accountFileName") . ": " . creationDate) - } else { - LogDebug("Filename createdAt outside expected range for " . session.get("accountFileName") . ": " . fileCreationDate) - } - } - - accountMeta["deviceAccount"] := GetCurrentDeviceAccountForMetadata() - accountMeta["createdAt"] := creationDate - session.set("accountCreatedAt", creationDate) - saveOk := AccountMetadata_SaveAccount(session.get("scriptName"), session.get("accountFileName"), accountMeta) - if (saveOk) - LogInfo("Saved account createdAt for " . session.get("accountFileName") . ": " . creationDate) - else - LogWarn("Failed to save account createdAt for " . session.get("accountFileName")) - return saveOk -} - -AccountCreationDate_Normalize(value) { - value := Trim(value) - if (RegExMatch(value, "^\d{14}$")) - return value - if (RegExMatch(value, "^\d{9,12}$")) - return AccountCreationDate_FromUnix(value + 0) - return "" -} - -AccountCreationDate_FromUnix(unixTimestamp) { - creationDate := "19700101000000" - EnvAdd, creationDate, %unixTimestamp%, Seconds - return creationDate -} - -AccountCreationDate_ToUnix(creationDate) { - unixTimestamp := creationDate - EnvSub, unixTimestamp, 19700101000000, Seconds - return unixTimestamp + 0 -} - -EnsureAccountLanguageMetadata() { - prof := Prof_Scope(A_ThisFunc) - global botConfig, session - - if (session.get("accountFileName") = "") - return false - if ((!session.get("injectMethod") || !session.get("loadedAccount")) && botConfig.get("deleteMethod") != "Create Bots (13P)") - return false - - deviceAccount := GetCurrentDeviceAccountForMetadata() - if (deviceAccount = "") { - LogWarn("EnsureAccountLanguageMetadata skipped because device account could not be resolved for " . session.get("accountFileName")) - return false - } - - LogTrace("Ensuring ptcgpb helper exists before language lookup", "ADB.txt") - if (!EnsurePTCGPBHelperInstalled()) - return false - - LogTrace("Running ptcgpb language lookup", "ADB.txt") - language := adbWriteRaw("/data/ptcgp/ptcgpb lang", true) - language := Trim(StrReplace(language, "`r"), "`n`t ") - if (language = "") { - LogWarn("EnsureAccountLanguageMetadata failed because ptcgpb lang returned no language") - return false - } - - if (!RegExMatch(language, "i)^[a-z][a-z0-9_-]{0,15}$")) { - LogWarn("EnsureAccountLanguageMetadata ignored unexpected language value: " . language) - return false - } - - session.set("language", language) - - if (!AccountMetadata_SetLanguage(deviceAccount, language, session.get("scriptName"), session.get("accountFileName"))) { - LogWarn("EnsureAccountLanguageMetadata failed to save language for " . session.get("accountFileName")) - return false - } - - LogInfo("Saved account language " . language . " for " . session.get("accountFileName")) - return true -} - -GetHistoryOfAccount() { - prof := Prof_Scope(A_ThisFunc) - global session, botConfig - - if (!session.get("injectMethod") || !session.get("loadedAccount") || session.get("accountFileName") = "") - return false - - LogInfo("GetHistoryOfAccount started for " . session.get("accountFileName")) - accountPath := A_ScriptDir . "\..\Accounts\Saved\" . session.get("scriptName") . "\" . session.get("accountFileName") - accountMeta := AccountMetadata_Get(session.get("scriptName"), session.get("accountFileName"), accountPath) - inDepthHistoryImport := botConfig.get("inDepthHistoryImport") - - deviceAccount := GetCurrentDeviceAccountForMetadata() - if (deviceAccount = "") { - LogWarn("GetHistoryOfAccount skipped because device account could not be resolved for " . session.get("accountFileName")) - return false - } - - if (!inDepthHistoryImport && AccountEligibility_FlagIsSet(accountMeta, "H") && AccountMetadata_AccountHasPulls(deviceAccount)) { - LogDebug("GetHistoryOfAccount skipped because history is already imported for " . session.get("accountFileName")) - return true - } - - helperPath := AccountMetadata_HelperPath() - if (!FileExist(helperPath)) { - LogWarn("GetHistoryOfAccount skipped because carddb helper is missing at " . helperPath) - return false - } - - CreateStatusMessage("Importing Pack History... Please wait a bit.") - LogDebug("Importing pack history for device account " . deviceAccount) - - safeName := RegExReplace(deviceAccount, "[^A-Za-z0-9_.-]", "_") - filename := "history_" . safeName . ".txt" - remotePath := "/data/ptcgp/" . filename - sdcardPath := "/sdcard/" . filename - localDir := A_ScriptDir . "\temp" - if (!FileExist(localDir)) - FileCreateDir, %localDir% - localPath := localDir . "\" . filename - if (FileExist(localPath)) - FileDelete, %localPath% - - LogTrace("Ensuring MissionUserPrefs exists before history import", "ADB.txt") - ensureMissionUserPrefsExist() - - adbCommand := session.get("adbPath") . " -s 127.0.0.1:" . session.get("adbPort") - LogTrace("Ensuring ptcgpb helper exists before history import", "ADB.txt") - if (!EnsurePTCGPBHelperInstalled()) - return false - LogTrace("Clearing stale remote history files: " . remotePath . " and " . sdcardPath, "ADB.txt") - adbWriteRaw("rm -f " . remotePath . " " . sdcardPath) - LogTrace("Running ptcgpb history export to " . remotePath, "ADB.txt") - adbWriteRaw("/data/ptcgp/ptcgpb history --out " . remotePath) - LogTrace("Copying history export to sdcard path " . sdcardPath, "ADB.txt") - adbWriteRaw("cp -f " . remotePath . " " . sdcardPath) - - LogTrace("Pulling history file to " . localPath, "ADB.txt") - RunWait, % """" . session.get("adbPath") . """ -s 127.0.0.1:" . session.get("adbPort") . " pull """ . sdcardPath . """ """ . localPath . """",, Hide - adbWriteRaw("rm -f " . remotePath . " " . sdcardPath) - if (!FileExist(localPath)) { - LogWarn("GetHistoryOfAccount failed because pulled history file was not created: " . localPath) - return false - } - - root := getScriptBaseFolder() - LogDebug("Importing history file with carddb for device account " . deviceAccount . (inDepthHistoryImport ? " (in-depth)" : "")) - importArgs := """" . helperPath . """ --root """ . root . """ import-history --device-account """ . deviceAccount . """ --input """ . localPath . """" - if (inDepthHistoryImport) - importArgs .= " --in-depth" - RunWait, %importArgs%,, Hide - if (ErrorLevel) { - LogWarn("GetHistoryOfAccount carddb import-history failed for " . session.get("accountFileName") . " ErrorLevel=" . ErrorLevel) - return false - } - - FileDelete, %localPath% - LogInfo("GetHistoryOfAccount completed for " . session.get("accountFileName")) - return true -} - InitPackOpening(full := false) { prof := Prof_Scope(A_ThisFunc) @@ -3471,255 +3216,6 @@ GetNeedle(Path) { } } -; Pick a username using AccountName prefix or usernames(_default).txt (same rules as Create Bots). -PickRenameUsername() { - global botConfig - - if (botConfig.get("AccountName") != "ERROR" && botConfig.get("AccountName") != "") { - Random, randomNum, 1, 500 - username := botConfig.get("AccountName") . "-" . randomNum - username := SubStr(username, 1, 14) - LogDebug("Rename using AccountName: " . username) - return username - } - - fileName := A_ScriptDir . "\..\usernames.txt" - if (FileExist(fileName)) - name := ReadFile("usernames") - else - name := ReadFile("usernames_default") - - Random, randomIndex, 1, name.MaxIndex() - username := name[randomIndex] - username := SubStr(username, 1, 14) - LogDebug("Rename using random username: " . username) - return username -} - -; True when current loaded account is old enough and outside rename cooldown. -; Uses metadata createdAt/lastRenamedAt; if createdAt was missing, relies on -; GetAccountCreationDate() (already run after boot) + session.accountCreatedAt. -AccountRename_CanRenameCurrentAccount() { - global session - - accountPath := session.get("loadedAccount") - if (accountPath = "" || session.get("accountFileName") = "") - return false - - accountMeta := AccountMetadata_Get(session.get("scriptName"), session.get("accountFileName"), accountPath) - createdAt := AccountMetadata_NormalizeCreatedAt(accountMeta["createdAt"]) - sessionCreatedAt := AccountMetadata_NormalizeCreatedAt(session.get("accountCreatedAt")) - if ((createdAt = "" || createdAt = "0") && sessionCreatedAt != "" && sessionCreatedAt != "0") { - createdAt := sessionCreatedAt - accountMeta["createdAt"] := createdAt - AccountMetadata_SaveAccount(session.get("scriptName"), session.get("accountFileName"), accountMeta) - } - if (createdAt != "" && createdAt != "0") - session.set("accountCreatedAt", createdAt) - - if (!AccountEligibility_RenameAccountEligible(accountMeta)) { - minDays := AccountEligibility_RenameMinAgeDays() - LogInfo("Rename Account: not eligible (need " . minDays . "d since create/rename) for " . session.get("accountFileName")) - return false - } - return true -} - -AccountRename_RecordCooldown(reason := "") { - global session - if (!session.get("injectMethod") || session.get("accountFileName") = "") - return - accountPath := session.get("loadedAccount") - if (accountPath = "") - return - accountMeta := AccountMetadata_Get(session.get("scriptName"), session.get("accountFileName"), accountPath) - accountMeta["lastRenamedAt"] := AccountMetadata_Now() - accountMeta["deviceAccount"] := GetCurrentDeviceAccountForMetadata() - AccountMetadata_SaveAccount(session.get("scriptName"), session.get("accountFileName"), accountMeta) - session.set("accountLastRenamedAt", accountMeta["lastRenamedAt"]) - if (reason != "") - LogInfo("Rename Account: recorded lastRenamedAt (" . reason . ") for " . session.get("accountFileName")) -} - -; Inject Rename Account: hamburger → profile → edit name → type username → confirm. -; Returns the applied username, or "" on failure/timeout. -DoRenameAccount() { - prof := Prof_Scope(A_ThisFunc) - global session - - CreateStatusMessage("Rename Account`nOpening profile...",,,, false) - - ; 1) Open hamburger until the settings-menu username arrow is visible. - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - if (FindOrLoseImage("Profile_UserNameArrowInSettingMenu", 0, 0, , true)) - break - adbClick_wbb(240, 494) ; hamburger menu button - Delay(1) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Rename Account`nOpening menu... (" . failSafeTime . "/30s)") - if (failSafeTime >= 30) { - LogWarn("Rename Account: timed out waiting for hamburger menu arrow") - return "" - } - } - - ; 2) Enter profile. Arrow gone = we left the hamburger / entered profile. - CreateStatusMessage("Rename Account`nEntering profile...",,,, false) - adbClick_wbb(242, 131) ; user profile row in hamburger - Delay(1) - session.set("failSafe", A_TickCount) - failSafeTime := 0 - lastProfileClick := A_TickCount - Loop { - if (!FindOrLoseImage("Profile_UserNameArrowInSettingMenu", 0, 0, , true)) - break - ; Still on menu — re-tap profile row, never the hamburger. - if (A_TickCount - lastProfileClick >= 1200) { - adbClick_wbb(242, 131) - lastProfileClick := A_TickCount - Delay(1) - } - Delay(0.25) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Rename Account`nWaiting profile... (" . failSafeTime . "/20s)") - if (failSafeTime >= 20) { - LogWarn("Rename Account: timed out waiting for profile entry (arrow still visible)") - return "" - } - } - - ; 3) Dismiss mission / tutorial overlays on profile (top-left). - CreateStatusMessage("Rename Account`nDismissing missions...",,,, false) - Loop, 4 { - adbClick_wbb(69, 156) - Delay(0.5) - } - - ; 4) Wait for UsernamePencil stable >= 3s (trophies/overlay can flash it). - session.set("failSafe", A_TickCount) - failSafeTime := 0 - pencilSeenAt := 0 - Loop { - if (FindOrLoseImage("Profile_UsernamePencil", 0, 0, 5, true)) { - if (pencilSeenAt = 0) - pencilSeenAt := A_TickCount - else if (A_TickCount - pencilSeenAt >= 3000) - break - } else { - pencilSeenAt := 0 - ; Extra dismiss taps while overlays may still be covering the pencil. - adbClick_wbb(69, 156) - Delay(0.4) - } - - Delay(0.25) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Rename Account`nFinding pencil... (" . failSafeTime . "/30s)") - if (failSafeTime >= 30) { - LogWarn("Rename Account: timed out waiting for UsernamePencil") - return "" - } - } - - CreateStatusMessage("Rename Account`nOpening name editor...",,,, false) - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - adbClick_wbb(136, 278) ; edit username row - Delay(1) - - ; 30-day rename cooldown: error popup, dismiss and skip this account. - if (FindOrLoseImage("Profile_AlreadyRenamed", 0, 0, 8, true)) { - CreateStatusMessage("Rename Account`nAlready renamed (30d) — skip",,,, false) - LogInfo("Rename Account: AlreadyRenamed cooldown — skipping account") - AccountRename_RecordCooldown("AlreadyRenamed popup") - adbClick_wbb(133, 367) - Delay(1) - return "" - } - - ; PreRename needle is a small blue curve — keep variation low to avoid false opens. - if (FindOrLoseImage("Profile_PreRename", 0, 0, 8, true)) - break - - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Rename Account`nWaiting PreRename... (" . failSafeTime . "/45s)") - if (failSafeTime >= 45) { - LogWarn("Rename Account: timed out waiting for PreRename") - return "" - } - } - - username := PickRenameUsername() - if (username = "") - return "" - - CreateStatusMessage("Rename Account`nTyping: " . username,,,, false) - adbClick_wbb(133, 260) ; focus editable text field - Delay(0.5) - ; Move cursor to the end, then select all and delete (same Shift+Home+Backspace - ; sequence as EraseInput in FriendManager). This works regardless of the - ; cursor position when the field is focused. - adbInputEvent("123") ; KEYCODE_MOVE_END - Delay(0.1) - Loop, 3 { - adbInputEvent("59 122 67") ; Shift+Home+Backspace - Delay(0.25) - } - adbInput(username) - Delay(1) - - ; Confirm OK a couple of times on the PreRename OK region. - Loop, 2 { - adbClick_wbb(146, 366) - Delay(1) - } - - ; Success only when pencil stays visible for >= 2s (avoids flash between the two OKs). - ; Keep tapping OK while waiting — the 2nd confirm dialog may not match PreRename. - session.set("failSafe", A_TickCount) - failSafeTime := 0 - pencilSeenAt := 0 - lastOkClick := 0 - Loop { - if (FindOrLoseImage("Profile_UsernamePencil", 0, 0, 5, true)) { - if (pencilSeenAt = 0) - pencilSeenAt := A_TickCount - else if (A_TickCount - pencilSeenAt >= 2000) { - CreateStatusMessage("Rename Account`nDone: " . username,,,, false) - LogInfo("DoRenameAccount applied username: " . username) - return username - } - } else { - pencilSeenAt := 0 - ; Pencil gone / still on OK dialog — press OK again. - if (A_TickCount - lastOkClick >= 800) { - adbClick_wbb(146, 366) - lastOkClick := A_TickCount - Delay(0.3) - } - } - - ; Also OK if PreRename needle is still up (first confirm page). - if (FindOrLoseImage("Profile_PreRename", 0, 0, 8, true) && A_TickCount - lastOkClick >= 800) { - pencilSeenAt := 0 - adbClick_wbb(146, 366) - lastOkClick := A_TickCount - Delay(0.3) - } - - Delay(0.25) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Rename Account`nConfirming rename... (" . failSafeTime . "/30s)") - if (failSafeTime >= 30) { - LogWarn("Rename Account: rename not confirmed (pencil not stable 2s)") - return "" - } - } -} - DoTutorial() { prof := Prof_Scope(A_ThisFunc) global botConfig, session @@ -5405,32 +4901,6 @@ DoWonderPickOnly() { SetWonderPickMetadataFlag() } -SetWonderPickMetadataFlag() { - global session - - if (!session.get("injectMethod") || !session.get("loadedAccount") || session.get("accountFileName") = "") - return - - validUntil := A_Now - validUntil += 24, Hours - AccountMetadata_SetFlag(session.get("scriptName"), session.get("accountFileName"), "W", 1, validUntil) -} - -SetSpendHourglassMetadataFlag() { - global session, botConfig - - if (!session.get("injectMethod") || !session.get("loadedAccount") || session.get("accountFileName") = "") - return - if (!botConfig.get("spendHourGlass")) - return - if (botConfig.get("deleteMethod") != "Inject 13P+") - return - - validUntil := A_Now - validUntil += 24, Hours - AccountMetadata_SetFlag(session.get("scriptName"), session.get("accountFileName"), "SH", 1, validUntil) -} - DoWonderPick() { global session diff --git a/Scripts/Include/BotAccountMeta.ahk b/Scripts/Include/BotAccountMeta.ahk new file mode 100644 index 00000000..802df406 --- /dev/null +++ b/Scripts/Include/BotAccountMeta.ahk @@ -0,0 +1,284 @@ +; Account metadata helpers used by the instance script + +SetBeginnerMissionsDone() { + global session + + session.get("missionDoneList")["beginnerMissionsDone"] := 1 + deviceAccount := GetCurrentDeviceAccountForMetadata() + if (deviceAccount != "") { + AccountMetadata_SetLastLoggedInNow(deviceAccount, session.get("scriptName"), session.get("accountFileName")) + if (session.get("accountFileName") != "") + AccountMetadata_SetFlag(session.get("scriptName"), session.get("accountFileName"), "H", 1) + } + + if (session.get("injectMethod") && session.get("loadedAccount")) { + setMetaData() + if (session.get("accountFileName") != "") + AccountMetadata_SetFlag(session.get("scriptName"), session.get("accountFileName"), "H", 1) + } +} + +SetLastPackPulledNow() { + global session + + deviceAccount := GetCurrentDeviceAccountForMetadata() + if (deviceAccount != "") + AccountMetadata_SetLastPackPulledNow(deviceAccount, session.get("scriptName"), session.get("accountFileName")) +} + +GetCurrentDeviceAccountForMetadata() { + global session + + deviceAccount := session.get("deviceAccount") + if (deviceAccount = "") { + LogDebug("Device account missing in session; reading from XML metadata") + deviceAccount := GetDeviceAccountFromXML() + if (deviceAccount != "") { + LogDebug("Resolved device account from XML for " . session.get("accountFileName")) + session.set("deviceAccount", deviceAccount) + } else { + LogWarn("Could not resolve device account for " . session.get("accountFileName")) + } + } + return deviceAccount +} + +GetAccountCreationDate() { + global session + + if (!session.get("injectMethod") || !session.get("loadedAccount") || session.get("accountFileName") = "") + return false + + LogDebug("GetAccountCreationDate started for " . session.get("accountFileName")) + accountPath := A_ScriptDir . "\..\Accounts\Saved\" . session.get("scriptName") . "\" . session.get("accountFileName") + accountMeta := AccountMetadata_Get(session.get("scriptName"), session.get("accountFileName"), accountPath) + existingCreatedAt := accountMeta["createdAt"] + + LogTrace("Ensuring ptcgpb helper exists before creation-date check", "ADB.txt") + if (!EnsurePTCGPBHelperInstalled()) + return false + + LogTrace("Running ptcgpb earliest for creation-date window", "ADB.txt") + earliestOutput := adbWriteRaw("/data/ptcgp/ptcgpb earliest", true) + earliestOutput := StrReplace(earliestOutput, "`r") + earliestOutput := Trim(earliestOutput, "`n`t ") + if (!RegExMatch(earliestOutput, "(\d{9,12})", earliestMatch)) { + LogWarn("GetAccountCreationDate could not parse earliest output for " . session.get("accountFileName") . ": " . earliestOutput) + return false + } + + earliestUnix := earliestMatch1 + 0 + latestUnix := earliestUnix + 86400 + LogDebug("Creation-date valid window for " . session.get("accountFileName") . ": earliestUnix=" . earliestUnix . ", latestUnix=" . latestUnix) + normalizedExistingCreatedAt := AccountCreationDate_Normalize(existingCreatedAt) + if (normalizedExistingCreatedAt != "") { + existingCreatedAtUnix := AccountCreationDate_ToUnix(normalizedExistingCreatedAt) + if (existingCreatedAtUnix >= earliestUnix && existingCreatedAtUnix <= latestUnix) + { + if (normalizedExistingCreatedAt != existingCreatedAt) { + LogDebug("Normalized account createdAt from Unix timestamp for " . session.get("accountFileName")) + accountMeta["createdAt"] := normalizedExistingCreatedAt + AccountMetadata_SaveAccount(session.get("scriptName"), session.get("accountFileName"), accountMeta) + } + session.set("accountCreatedAt", normalizedExistingCreatedAt) + LogDebug("Using existing account createdAt for " . session.get("accountFileName")) + return true + } + LogDebug("Existing account createdAt outside expected range for " . session.get("accountFileName")) + } + + creationDate := AccountCreationDate_FromUnix(latestUnix) + LogDebug("Defaulting createdAt to latest window bound for " . session.get("accountFileName") . ": " . creationDate) + + if (RegExMatch(session.get("accountFileName"), "^\d+P_(\d{14})_", fileMatch)) { + fileCreationDate := fileMatch1 + fileCreationUnix := AccountCreationDate_ToUnix(fileCreationDate) + if (fileCreationUnix >= earliestUnix && fileCreationUnix <= latestUnix) { + creationDate := fileCreationDate + LogDebug("Using createdAt from filename for " . session.get("accountFileName") . ": " . creationDate) + } else { + LogDebug("Filename createdAt outside expected range for " . session.get("accountFileName") . ": " . fileCreationDate) + } + } + + accountMeta["deviceAccount"] := GetCurrentDeviceAccountForMetadata() + accountMeta["createdAt"] := creationDate + session.set("accountCreatedAt", creationDate) + saveOk := AccountMetadata_SaveAccount(session.get("scriptName"), session.get("accountFileName"), accountMeta) + if (saveOk) + LogInfo("Saved account createdAt for " . session.get("accountFileName") . ": " . creationDate) + else + LogWarn("Failed to save account createdAt for " . session.get("accountFileName")) + return saveOk +} + +AccountCreationDate_Normalize(value) { + value := Trim(value) + if (RegExMatch(value, "^\d{14}$")) + return value + if (RegExMatch(value, "^\d{9,12}$")) + return AccountCreationDate_FromUnix(value + 0) + return "" +} + +AccountCreationDate_FromUnix(unixTimestamp) { + creationDate := "19700101000000" + EnvAdd, creationDate, %unixTimestamp%, Seconds + return creationDate +} + +AccountCreationDate_ToUnix(creationDate) { + unixTimestamp := creationDate + EnvSub, unixTimestamp, 19700101000000, Seconds + return unixTimestamp + 0 +} + +EnsureAccountLanguageMetadata() { + prof := Prof_Scope(A_ThisFunc) + global botConfig, session + + if (session.get("accountFileName") = "") + return false + if ((!session.get("injectMethod") || !session.get("loadedAccount")) && botConfig.get("deleteMethod") != "Create Bots (13P)") + return false + + deviceAccount := GetCurrentDeviceAccountForMetadata() + if (deviceAccount = "") { + LogWarn("EnsureAccountLanguageMetadata skipped because device account could not be resolved for " . session.get("accountFileName")) + return false + } + + LogTrace("Ensuring ptcgpb helper exists before language lookup", "ADB.txt") + if (!EnsurePTCGPBHelperInstalled()) + return false + + LogTrace("Running ptcgpb language lookup", "ADB.txt") + language := adbWriteRaw("/data/ptcgp/ptcgpb lang", true) + language := Trim(StrReplace(language, "`r"), "`n`t ") + if (language = "") { + LogWarn("EnsureAccountLanguageMetadata failed because ptcgpb lang returned no language") + return false + } + + if (!RegExMatch(language, "i)^[a-z][a-z0-9_-]{0,15}$")) { + LogWarn("EnsureAccountLanguageMetadata ignored unexpected language value: " . language) + return false + } + + session.set("language", language) + + if (!AccountMetadata_SetLanguage(deviceAccount, language, session.get("scriptName"), session.get("accountFileName"))) { + LogWarn("EnsureAccountLanguageMetadata failed to save language for " . session.get("accountFileName")) + return false + } + + LogInfo("Saved account language " . language . " for " . session.get("accountFileName")) + return true +} + +GetHistoryOfAccount() { + prof := Prof_Scope(A_ThisFunc) + global session, botConfig + + if (!session.get("injectMethod") || !session.get("loadedAccount") || session.get("accountFileName") = "") + return false + + LogInfo("GetHistoryOfAccount started for " . session.get("accountFileName")) + accountPath := A_ScriptDir . "\..\Accounts\Saved\" . session.get("scriptName") . "\" . session.get("accountFileName") + accountMeta := AccountMetadata_Get(session.get("scriptName"), session.get("accountFileName"), accountPath) + inDepthHistoryImport := botConfig.get("inDepthHistoryImport") + + deviceAccount := GetCurrentDeviceAccountForMetadata() + if (deviceAccount = "") { + LogWarn("GetHistoryOfAccount skipped because device account could not be resolved for " . session.get("accountFileName")) + return false + } + + if (!inDepthHistoryImport && AccountEligibility_FlagIsSet(accountMeta, "H") && AccountMetadata_AccountHasPulls(deviceAccount)) { + LogDebug("GetHistoryOfAccount skipped because history is already imported for " . session.get("accountFileName")) + return true + } + + helperPath := AccountMetadata_HelperPath() + if (!FileExist(helperPath)) { + LogWarn("GetHistoryOfAccount skipped because carddb helper is missing at " . helperPath) + return false + } + + CreateStatusMessage("Importing Pack History... Please wait a bit.") + LogDebug("Importing pack history for device account " . deviceAccount) + + safeName := RegExReplace(deviceAccount, "[^A-Za-z0-9_.-]", "_") + filename := "history_" . safeName . ".txt" + remotePath := "/data/ptcgp/" . filename + sdcardPath := "/sdcard/" . filename + localDir := A_ScriptDir . "\temp" + if (!FileExist(localDir)) + FileCreateDir, %localDir% + localPath := localDir . "\" . filename + if (FileExist(localPath)) + FileDelete, %localPath% + + LogTrace("Ensuring MissionUserPrefs exists before history import", "ADB.txt") + ensureMissionUserPrefsExist() + + adbCommand := session.get("adbPath") . " -s 127.0.0.1:" . session.get("adbPort") + LogTrace("Ensuring ptcgpb helper exists before history import", "ADB.txt") + if (!EnsurePTCGPBHelperInstalled()) + return false + LogTrace("Clearing stale remote history files: " . remotePath . " and " . sdcardPath, "ADB.txt") + adbWriteRaw("rm -f " . remotePath . " " . sdcardPath) + LogTrace("Running ptcgpb history export to " . remotePath, "ADB.txt") + adbWriteRaw("/data/ptcgp/ptcgpb history --out " . remotePath) + LogTrace("Copying history export to sdcard path " . sdcardPath, "ADB.txt") + adbWriteRaw("cp -f " . remotePath . " " . sdcardPath) + + LogTrace("Pulling history file to " . localPath, "ADB.txt") + RunWait, % """" . session.get("adbPath") . """ -s 127.0.0.1:" . session.get("adbPort") . " pull """ . sdcardPath . """ """ . localPath . """",, Hide + adbWriteRaw("rm -f " . remotePath . " " . sdcardPath) + if (!FileExist(localPath)) { + LogWarn("GetHistoryOfAccount failed because pulled history file was not created: " . localPath) + return false + } + + root := getScriptBaseFolder() + LogDebug("Importing history file with carddb for device account " . deviceAccount . (inDepthHistoryImport ? " (in-depth)" : "")) + importArgs := """" . helperPath . """ --root """ . root . """ import-history --device-account """ . deviceAccount . """ --input """ . localPath . """" + if (inDepthHistoryImport) + importArgs .= " --in-depth" + RunWait, %importArgs%,, Hide + if (ErrorLevel) { + LogWarn("GetHistoryOfAccount carddb import-history failed for " . session.get("accountFileName") . " ErrorLevel=" . ErrorLevel) + return false + } + + FileDelete, %localPath% + LogInfo("GetHistoryOfAccount completed for " . session.get("accountFileName")) + return true +} + +SetWonderPickMetadataFlag() { + global session + + if (!session.get("injectMethod") || !session.get("loadedAccount") || session.get("accountFileName") = "") + return + + validUntil := A_Now + validUntil += 24, Hours + AccountMetadata_SetFlag(session.get("scriptName"), session.get("accountFileName"), "W", 1, validUntil) +} + +SetSpendHourglassMetadataFlag() { + global session, botConfig + + if (!session.get("injectMethod") || !session.get("loadedAccount") || session.get("accountFileName") = "") + return + if (!botConfig.get("spendHourGlass")) + return + if (botConfig.get("deleteMethod") != "Inject 13P+") + return + + validUntil := A_Now + validUntil += 24, Hours + AccountMetadata_SetFlag(session.get("scriptName"), session.get("accountFileName"), "SH", 1, validUntil) +} diff --git a/Scripts/Include/BotAccountRename.ahk b/Scripts/Include/BotAccountRename.ahk new file mode 100644 index 00000000..b6fd43a4 --- /dev/null +++ b/Scripts/Include/BotAccountRename.ahk @@ -0,0 +1,250 @@ +; Account rename flow used by the instance script + +; Pick a username using AccountName prefix or usernames(_default).txt (same rules as Create Bots). +PickRenameUsername() { + global botConfig + + if (botConfig.get("AccountName") != "ERROR" && botConfig.get("AccountName") != "") { + Random, randomNum, 1, 500 + username := botConfig.get("AccountName") . "-" . randomNum + username := SubStr(username, 1, 14) + LogDebug("Rename using AccountName: " . username) + return username + } + + fileName := A_ScriptDir . "\..\usernames.txt" + if (FileExist(fileName)) + name := ReadFile("usernames") + else + name := ReadFile("usernames_default") + + Random, randomIndex, 1, name.MaxIndex() + username := name[randomIndex] + username := SubStr(username, 1, 14) + LogDebug("Rename using random username: " . username) + return username +} + +; True when current loaded account is old enough and outside rename cooldown. +; Uses metadata createdAt/lastRenamedAt; if createdAt was missing, relies on +; GetAccountCreationDate() (already run after boot) + session.accountCreatedAt. +AccountRename_CanRenameCurrentAccount() { + global session + + accountPath := session.get("loadedAccount") + if (accountPath = "" || session.get("accountFileName") = "") + return false + + accountMeta := AccountMetadata_Get(session.get("scriptName"), session.get("accountFileName"), accountPath) + createdAt := AccountMetadata_NormalizeCreatedAt(accountMeta["createdAt"]) + sessionCreatedAt := AccountMetadata_NormalizeCreatedAt(session.get("accountCreatedAt")) + if ((createdAt = "" || createdAt = "0") && sessionCreatedAt != "" && sessionCreatedAt != "0") { + createdAt := sessionCreatedAt + accountMeta["createdAt"] := createdAt + AccountMetadata_SaveAccount(session.get("scriptName"), session.get("accountFileName"), accountMeta) + } + if (createdAt != "" && createdAt != "0") + session.set("accountCreatedAt", createdAt) + + if (!AccountEligibility_RenameAccountEligible(accountMeta)) { + minDays := AccountEligibility_RenameMinAgeDays() + LogInfo("Rename Account: not eligible (need " . minDays . "d since create/rename) for " . session.get("accountFileName")) + return false + } + return true +} + +AccountRename_RecordCooldown(reason := "") { + global session + if (!session.get("injectMethod") || session.get("accountFileName") = "") + return + accountPath := session.get("loadedAccount") + if (accountPath = "") + return + accountMeta := AccountMetadata_Get(session.get("scriptName"), session.get("accountFileName"), accountPath) + accountMeta["lastRenamedAt"] := AccountMetadata_Now() + accountMeta["deviceAccount"] := GetCurrentDeviceAccountForMetadata() + AccountMetadata_SaveAccount(session.get("scriptName"), session.get("accountFileName"), accountMeta) + session.set("accountLastRenamedAt", accountMeta["lastRenamedAt"]) + if (reason != "") + LogInfo("Rename Account: recorded lastRenamedAt (" . reason . ") for " . session.get("accountFileName")) +} + +; Inject Rename Account: hamburger → profile → edit name → type username → confirm. +; Returns the applied username, or "" on failure/timeout. +DoRenameAccount() { + prof := Prof_Scope(A_ThisFunc) + global session + + CreateStatusMessage("Rename Account`nOpening profile...",,,, false) + + ; 1) Open hamburger until the settings-menu username arrow is visible. + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + if (FindOrLoseImage("Profile_UserNameArrowInSettingMenu", 0, 0, , true)) + break + adbClick_wbb(240, 494) ; hamburger menu button + Delay(1) + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Rename Account`nOpening menu... (" . failSafeTime . "/30s)") + if (failSafeTime >= 30) { + LogWarn("Rename Account: timed out waiting for hamburger menu arrow") + return "" + } + } + + ; 2) Enter profile. Arrow gone = we left the hamburger / entered profile. + CreateStatusMessage("Rename Account`nEntering profile...",,,, false) + adbClick_wbb(242, 131) ; user profile row in hamburger + Delay(1) + session.set("failSafe", A_TickCount) + failSafeTime := 0 + lastProfileClick := A_TickCount + Loop { + if (!FindOrLoseImage("Profile_UserNameArrowInSettingMenu", 0, 0, , true)) + break + ; Still on menu — re-tap profile row, never the hamburger. + if (A_TickCount - lastProfileClick >= 1200) { + adbClick_wbb(242, 131) + lastProfileClick := A_TickCount + Delay(1) + } + Delay(0.25) + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Rename Account`nWaiting profile... (" . failSafeTime . "/20s)") + if (failSafeTime >= 20) { + LogWarn("Rename Account: timed out waiting for profile entry (arrow still visible)") + return "" + } + } + + ; 3) Dismiss mission / tutorial overlays on profile (top-left). + CreateStatusMessage("Rename Account`nDismissing missions...",,,, false) + Loop, 4 { + adbClick_wbb(69, 156) + Delay(0.5) + } + + ; 4) Wait for UsernamePencil stable >= 3s (trophies/overlay can flash it). + session.set("failSafe", A_TickCount) + failSafeTime := 0 + pencilSeenAt := 0 + Loop { + if (FindOrLoseImage("Profile_UsernamePencil", 0, 0, 5, true)) { + if (pencilSeenAt = 0) + pencilSeenAt := A_TickCount + else if (A_TickCount - pencilSeenAt >= 3000) + break + } else { + pencilSeenAt := 0 + ; Extra dismiss taps while overlays may still be covering the pencil. + adbClick_wbb(69, 156) + Delay(0.4) + } + + Delay(0.25) + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Rename Account`nFinding pencil... (" . failSafeTime . "/30s)") + if (failSafeTime >= 30) { + LogWarn("Rename Account: timed out waiting for UsernamePencil") + return "" + } + } + + CreateStatusMessage("Rename Account`nOpening name editor...",,,, false) + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + adbClick_wbb(136, 278) ; edit username row + Delay(1) + + ; 30-day rename cooldown: error popup, dismiss and skip this account. + if (FindOrLoseImage("Profile_AlreadyRenamed", 0, 0, 8, true)) { + CreateStatusMessage("Rename Account`nAlready renamed (30d) — skip",,,, false) + LogInfo("Rename Account: AlreadyRenamed cooldown — skipping account") + AccountRename_RecordCooldown("AlreadyRenamed popup") + adbClick_wbb(133, 367) + Delay(1) + return "" + } + + ; PreRename needle is a small blue curve — keep variation low to avoid false opens. + if (FindOrLoseImage("Profile_PreRename", 0, 0, 8, true)) + break + + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Rename Account`nWaiting PreRename... (" . failSafeTime . "/45s)") + if (failSafeTime >= 45) { + LogWarn("Rename Account: timed out waiting for PreRename") + return "" + } + } + + username := PickRenameUsername() + if (username = "") + return "" + + CreateStatusMessage("Rename Account`nTyping: " . username,,,, false) + adbClick_wbb(133, 260) ; focus editable text field + Delay(0.5) + ; Move cursor to the end, then select all and delete (same Shift+Home+Backspace + ; sequence as EraseInput in FriendManager). This works regardless of the + ; cursor position when the field is focused. + adbInputEvent("123") ; KEYCODE_MOVE_END + Delay(0.1) + Loop, 3 { + adbInputEvent("59 122 67") ; Shift+Home+Backspace + Delay(0.25) + } + adbInput(username) + Delay(1) + + ; Confirm OK a couple of times on the PreRename OK region. + Loop, 2 { + adbClick_wbb(146, 366) + Delay(1) + } + + ; Success only when pencil stays visible for >= 2s (avoids flash between the two OKs). + ; Keep tapping OK while waiting — the 2nd confirm dialog may not match PreRename. + session.set("failSafe", A_TickCount) + failSafeTime := 0 + pencilSeenAt := 0 + lastOkClick := 0 + Loop { + if (FindOrLoseImage("Profile_UsernamePencil", 0, 0, 5, true)) { + if (pencilSeenAt = 0) + pencilSeenAt := A_TickCount + else if (A_TickCount - pencilSeenAt >= 2000) { + CreateStatusMessage("Rename Account`nDone: " . username,,,, false) + LogInfo("DoRenameAccount applied username: " . username) + return username + } + } else { + pencilSeenAt := 0 + ; Pencil gone / still on OK dialog — press OK again. + if (A_TickCount - lastOkClick >= 800) { + adbClick_wbb(146, 366) + lastOkClick := A_TickCount + Delay(0.3) + } + } + + ; Also OK if PreRename needle is still up (first confirm page). + if (FindOrLoseImage("Profile_PreRename", 0, 0, 8, true) && A_TickCount - lastOkClick >= 800) { + pencilSeenAt := 0 + adbClick_wbb(146, 366) + lastOkClick := A_TickCount + Delay(0.3) + } + + Delay(0.25) + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Rename Account`nConfirming rename... (" . failSafeTime . "/30s)") + if (failSafeTime >= 30) { + LogWarn("Rename Account: rename not confirmed (pencil not stable 2s)") + return "" + } + } +} From 6d02673d00cc4cbcf2a15b188450bc5c60d4200e Mon Sep 17 00:00:00 2001 From: Lean Date: Sun, 4 Oct 2026 15:11:32 +0200 Subject: [PATCH 04/10] Move toggles and pack functions --- Scripts/1.ahk | 3724 ++++--------------- Scripts/Include/BotPackEvaluation.ahk | 877 +++++ Scripts/Include/BotPackOpening.ahk | 1025 +++++ Scripts/Include/BotStopSignals.ahk | 74 + Scripts/Include/BotToggles.ahk | 144 + Scripts/Include/Crinity_UnofficialPatch.ahk | 1 + 6 files changed, 2927 insertions(+), 2918 deletions(-) create mode 100644 Scripts/Include/BotPackEvaluation.ahk create mode 100644 Scripts/Include/BotPackOpening.ahk create mode 100644 Scripts/Include/BotStopSignals.ahk create mode 100644 Scripts/Include/BotToggles.ahk diff --git a/Scripts/1.ahk b/Scripts/1.ahk index ae06cacb..7a332396 100644 --- a/Scripts/1.ahk +++ b/Scripts/1.ahk @@ -16,6 +16,10 @@ pToken := Gdip_Startup() #Include HourglassSpend.ahk #Include BotAccountMeta.ahk #Include BotAccountRename.ahk +#Include BotToggles.ahk +#Include BotStopSignals.ahk +#Include BotPackEvaluation.ahk +#Include BotPackOpening.ahk InitializeHiddenConsole() @@ -1313,12 +1317,6 @@ DirectlyPositionWindow() { return true } -PersistStopAfterRunIfNeeded() { - global session - if (session.get("stopToggle")) - IniWrite, 1, % session.get("scriptIniFile"), UserSettings, stopAfterRunPending -} - FinalizeInjectedGodPackAccount(testing := True) { global botConfig, session, DeadCheck @@ -1589,2685 +1587,813 @@ menuDeleteStart() { } } -InitPackOpening(full := false) { - prof := Prof_Scope(A_ThisFunc) - - if (!EnsurePTCGPBHelperInstalled()) - return false - adbWriteRaw("rm -f /data/ptcgp/result.rc") - adbWriteRaw("rm -f /data/ptcgp/result.log") - adbWriteRaw("pkill -f /data/ptcgp/ptcgpb") - - SavePackOpeningMissionUserPrefsSnapshot("pre") - StartPtcgpbWatchCards(full) +ControlClick(X, Y) { + global session + ControlClick, x%X% y%Y%, % session.get("winTitle") } -PackOpeningMissionUserPrefsPath() { - return "/data/data/jp.pokemon.pokemontcgp/files/UserPreferences/v1/MissionUserPrefs" -} +Screenshot_dev(fileType := "Dev", subDir := "", srcPath := "") { + prof := Prof_Scope(A_ThisFunc) + global session, rec_Active + SetWorkingDir %A_ScriptDir% ; Ensures the working directory is the script's directory -PackOpeningMissionUserPrefsSnapshotPath(kind) { - global session - return "/data/ptcgp/" . session.get("scriptName") . "_" . kind -} + ; Define folder and file paths + fileDir := A_ScriptDir "\..\Screenshots\grab" + if !FileExist(fileDir) + FileCreateDir, %fileDir% + if (subDir) { + fileDir .= "\" . subDir + } + if !FileExist(fileDir) + FileCreateDir, %fileDir% -SavePackOpeningMissionUserPrefsSnapshot(kind) { - sourcePath := PackOpeningMissionUserPrefsPath() - snapshotPath := PackOpeningMissionUserPrefsSnapshotPath(kind) + ; File path for saving the screenshot locally + fileName := A_Now . "_" . session.get("scriptName") . "_" . fileType . ".png" + filePath := fileDir "\" . fileName - adbWriteRaw("mkdir -p /data/ptcgp") - adbWriteRaw("rm -f " . snapshotPath) - adbWriteRaw("if [ -f " . sourcePath . " ]; then cp -f " . sourcePath . " " . snapshotPath . "; fi") -} + if (srcPath != "" && FileExist(srcPath)) + pBitmapW := Gdip_CreateBitmapFromFile(srcPath) + else + pBitmapW := from_window(getMuMuHwnd(session.get("winTitle"))) + Gdip_SaveBitmapToFile(pBitmapW, filePath) -PullPackOpeningMissionUserPrefsSnapshot(kind, failedDir, uniquePrefix) { - global session + sleep 100 - if !FileExist(failedDir) - FileCreateDir, %failedDir% + try { + CoordMode, Mouse, Client + OwnerWND := WinExist(session.get("winTitle")) + buttonWidth := 40 - remotePath := PackOpeningMissionUserPrefsSnapshotPath(kind) - sdcardPath := "/sdcard/" . uniquePrefix . "_" . kind . "_MissionUserPrefs" - localPath := failedDir . "\" . uniquePrefix . "_" . kind + guiSuffix := session.get("winTitle") + Gui, DevMode_ss%guiSuffix%:New, +LastFound -DPIScale + Gui, DevMode_ss%guiSuffix%:Add, Picture, x0 y0 w275 h528 hwndhAppScreen, %filePath% + Gui, DevMode_ss%guiSuffix%:Show, w275 h528, % "Screensho" session.get("winTitle") - if (FileExist(localPath)) - FileDelete, %localPath% + GuiControlGet, PicPos, Pos, %hAppScreen% - adbWriteRaw("rm -f " . sdcardPath) - adbWriteRaw("if [ -f " . remotePath . " ]; then cp -f " . remotePath . " " . sdcardPath . " && chmod 666 " . sdcardPath . "; fi") - RunWait, % """" . session.get("adbPath") . """ -s 127.0.0.1:" . session.get("adbPort") . " pull """ . sdcardPath . """ """ . localPath . """",, Hide - adbWriteRaw("rm -f " . sdcardPath) + sleep 100 + msgbox click on top-left corner and bottom-right corners - exists := FileExist(localPath) ? true : false - return { kind: kind, remotePath: remotePath, localPath: localPath, exists: exists } -} + yBias := 40 -PullPackOpeningResultLog(failedDir, uniquePrefix) { - global session + KeyWait, LButton, D + MouseGetPos , X1, Y1, OutputVarWin, OutputVarControl + KeyWait, LButton, U + X1 := X1 - PicPosX + Y1 := Y1 - PicPosY - debugLogFile := "debug_cards.txt" - if !FileExist(failedDir) - FileCreateDir, %failedDir% + ; Return in case of user close the screen + if !WinExist("Screensho" session.get("winTitle")) + return - remotePath := "/data/ptcgp/result.log" - sdcardPath := "/sdcard/" . uniquePrefix . "_result.log" - localPath := failedDir . "\" . uniquePrefix . "_result.log" + KeyWait, LButton, D + MouseGetPos , X2, Y2, OutputVarWin, OutputVarControl + KeyWait, LButton, U + X2 := X2 - PicPosX + Y2 := Y2 - PicPosY - remoteStatus := Trim(StrReplace(adbWriteRaw("if [ -f " . remotePath . " ]; then echo present:$(wc -c < " . remotePath . "); else echo missing; fi", true), "`r"), "`n`t ") - LogWarn("PullPackOpeningResultLog begin | remote=" . remotePath . " | remoteStatus=" . remoteStatus . " | staging=" . sdcardPath . " | local=" . localPath, debugLogFile) + W:=X2-X1 + H:=Y2-Y1 - localDeleteStatus := "not-needed" - if (FileExist(localPath)) { - FileDelete, %localPath% - localDeleteStatus := ErrorLevel ? "failed" : "ok" - } + MsgBox, % X1 ", " Y1 " / " X2 ", " Y2 - stagingCleanupStatus := Trim(StrReplace(adbWriteRaw("if rm -f " . sdcardPath . "; then echo ok; else echo failed; fi", true), "`r"), "`n`t ") - stagingCopyStatus := Trim(StrReplace(adbWriteRaw("if [ ! -f " . remotePath . " ]; then echo remote-missing; elif cp -f " . remotePath . " " . sdcardPath . " && chmod 666 " . sdcardPath . "; then echo ok; else echo failed; fi", true), "`r"), "`n`t ") - RunWait, % """" . session.get("adbPath") . """ -s 127.0.0.1:" . session.get("adbPort") . " pull """ . sdcardPath . """ """ . localPath . """",, Hide - pullExitCode := ErrorLevel - finalCleanupStatus := Trim(StrReplace(adbWriteRaw("if rm -f " . sdcardPath . "; then echo ok; else echo failed; fi", true), "`r"), "`n`t ") - - exists := FileExist(localPath) ? true : false - localSize := 0 - resultLogContent := "(local result.log is missing)" - readSucceeded := false - if (exists) { - FileGetSize, localSize, %localPath% - FileRead, resultLogContent, %localPath% - readError := ErrorLevel - readSucceeded := !readError - if (!readSucceeded) - resultLogContent := "(failed to read local result.log; ErrorLevel=" . readError . ")" - else if (resultLogContent = "") - resultLogContent := "(empty)" - } + pBitmap := Gdip_CloneBitmapArea(pBitmapW, X1, Y1, W, H) - LogWarn("PullPackOpeningResultLog transfer | localDelete=" . localDeleteStatus . " | initialCleanup=" . stagingCleanupStatus . " | stagingCopy=" . stagingCopyStatus . " | adbPullExitCode=" . pullExitCode . " | finalCleanup=" . finalCleanupStatus . " | localExists=" . (exists ? "yes" : "no") . " | localBytes=" . localSize . " | read=" . (readSucceeded ? "ok" : "unavailable"), debugLogFile) - LogWarn("PullPackOpeningResultLog contents begin`n" . resultLogContent . "`nPullPackOpeningResultLog contents end", debugLogFile) + InputBox, fileName, ,"Enter the name of the needle to save" - return { kind: "result.log", remotePath: remotePath, localPath: localPath, exists: exists, remoteStatus: remoteStatus, pullExitCode: pullExitCode, localSize: localSize, readSucceeded: readSucceeded } -} + if (fileName = "") { + return "" + } -ReportPackRecognitionFailure(reason := "Card Recognition Failed, use fallback mechanism") { - global session, botConfig + fullScreenPath := filePath + fileDir := A_ScriptDir . "\Needles" + filePath := fileDir "\" . fileName . ".png" + Gdip_SaveBitmapToFile(pBitmap, filePath) - root := getScriptBaseFolder() - failedDir := root . "\Logs\failed" - uniquePrefix := A_Now . "_" . session.get("scriptName") . "_pack" . session.get("packsInPool") + KeyWait, LButton, D + MouseGetPos , X3, Y3, OutputVarWin, OutputVarControl + KeyWait, LButton, U + X3 := X3 - PicPosX + Y3 := Y3 - PicPosY - preSnapshot := PullPackOpeningMissionUserPrefsSnapshot("pre", failedDir, uniquePrefix) - postSnapshot := PullPackOpeningMissionUserPrefsSnapshot("post", failedDir, uniquePrefix) - resultLog := PullPackOpeningResultLog(failedDir, uniquePrefix) + global rec_LastScreenGrab + rec_LastScreenGrab := {fileName: fileName, needlePath: filePath + , screenshot: fullScreenPath + , x1: X1, y1: Y1, x2: X2, y2: Y2, x3: X3, y3: Y3} - message := reason . "\nPlease submit these files for the bug report as well." - for _, snapshot in [preSnapshot, postSnapshot] { - localPathForMessage := StrReplace(snapshot.localPath, "\", "/") - if (snapshot.exists) { - message .= "\n" . snapshot.kind . ": " . localPathForMessage - LogWarn("Card recognition failure " . snapshot.kind . " MissionUserPrefs saved: " . snapshot.localPath, "debug_cards.txt") - } else { - message .= "\n" . snapshot.kind . ": missing (remote " . snapshot.remotePath . " was not available)" - LogWarn("Card recognition failure " . snapshot.kind . " MissionUserPrefs missing: " . snapshot.remotePath, "debug_cards.txt") - } - } + ; Convert window coordinates to device/OCR coordinates + ; Device resolution: 540x960, Window resolution: 277x489, Y offset: 44 + OCR_X1 := Round(X1 * 540 / 283) + OCR_Y1 := Round((Y1 - 44) * 960 / 488) + OCR_W := Round(W * 540 / 283) + OCR_H := Round(H * 960 / 488) + OCR_X2 := OCR_X1 + OCR_W + OCR_Y2 := OCR_Y1 + OCR_H - resultLogPathForMessage := StrReplace(resultLog.localPath, "\", "/") - if (resultLog.exists) { - message .= "\n" . resultLog.kind . ": " . resultLogPathForMessage - LogWarn("Card recognition failure result.log saved: " . resultLog.localPath, "debug_cards.txt") - } else { - message .= "\n" . resultLog.kind . ": missing (remote " . resultLog.remotePath . " was not available)" - LogWarn("Card recognition failure result.log missing: " . resultLog.remotePath, "debug_cards.txt") - } + ; Calculate center point of the box + OCR_X3 := Round(OCR_X1 + OCR_W / 2) + OCR_Y3 := Round(OCR_Y1 + OCR_H / 2) - ownerWebhookURL := botConfig.get("heartBeatOwnerWebHookURL") - if (ownerWebhookURL != "") - LogToDiscord(message,, false,,, ownerWebhookURL) - else - LogWarn("Owner Discord webhook URL is not configured. Card recognition failure message was not sent.", "Discord.txt") + MsgBox, + (LTrim + ctrl+C to copy: + FindOrLoseImage(%X1%, %Y1%, %X2%, %Y2%, , "%fileName%", 0, failSafeTime) + FindImageAndClick(%X1%, %Y1%, %X2%, %Y2%, , "%fileName%", %X3%, %Y3%, sleepTime) + adbClick_wbb(%X3%, %Y3%) + OCR coordinates: %OCR_X3%, %OCR_Y3%, %OCR_W%, %OCR_H% + ) } - -GetStdout(cmd) { - prof := Prof_Scope(A_ThisFunc) - shell := ComObjCreate("WScript.Shell") - exec := shell.Exec(cmd) - return exec.StdOut.ReadAll() +catch { + msgbox Failed to create screenshot GUI } - -TerminateHelper() { - adbWriteRaw("pkill -f /data/ptcgp/ptcgpb") +CoordMode, Pixel, Screen +return filePath } -ParsePackResultOutput(output) { - output := StrReplace(output, "`r") - output := Trim(output, "`n ") - lines := StrSplit(output, "`n") - parsedLines := [] - for _, line in lines { - line := Trim(line) - if (line != "") - parsedLines.Push(line) - } - lines := parsedLines - - if (lines.Length() < 4) - return false - - pulls := [] - cards := [] - rarity := [] - packNames := [] - shinedust := 0 - raw_msg := "" - - idx := 1 - while (idx + 2 <= lines.Length()) { - pullCards := [] - for _, val in StrSplit(lines[idx], ",") { - card := Trim(val) - if (card != "") - pullCards.Push(card) - } - - pullPack := Trim(lines[idx + 1]) - - pullRarity := [] - for _, val in StrSplit(lines[idx + 2], ",") { - pullRarity.Push(Trim(val) + 0) - } - - if (pullCards.Length() = 0 || pullRarity.Length() = 0) - break - - pullShinedust := lines[idx + 3] + 0 - shinedust += pullShinedust +Screenshot(fileType := "Valid", subDir := "", ByRef fileName := "") { + prof := Prof_Scope(A_ThisFunc) + SetWorkingDir %A_ScriptDir% ; Ensures the working directory is the script's directory - pullRaw := lines[idx] . "`n" . lines[idx + 1] . "`n" . lines[idx + 2] . "`n" . lines[idx + 3] - pulls.Push({ cards: pullCards, pack: pullPack, rarity: pullRarity, shinedust: pullShinedust, raw: pullRaw }) - packNames.Push(pullPack) + ; Define folder and file paths + fileDir := A_ScriptDir "\..\Screenshots" + if !FileExist(fileDir) + FileCreateDir, %fileDir% + if (subDir) { + fileDir .= "\" . subDir + if !FileExist(fileDir) + FileCreateDir, %fileDir% + } + if (filename = "PACKSTATS") { + fileDir .= "\temp" + if !FileExist(fileDir) + FileCreateDir, %fileDir% + } - for _, card in pullCards - cards.Push(card) - for _, rare in pullRarity - rarity.Push(rare) + ; File path for saving the screenshot locally + fileName := A_Now . "_" . session.get("scriptName") . "_" . fileType . "_" . session.get("packsInPool") . "_packs.png" + if (filename = "PACKSTATS") + fileName := "packstats_temp.png" + filePath := fileDir "\" . fileName - if (raw_msg != "") - raw_msg .= "`n" - raw_msg .= pullRaw + yBias := 40 + cropX := 18 + cropY := 170 + cropW := 240 + cropH := 227 - idx += 4 + if (fileType = "FRIENDCODE"){ + cropX := 18 + cropY := 66 + cropW := 240 + cropH := 165 } - if (pulls.Length() = 0) - return false + pBitmapW := from_window(getMuMuHwnd(session.get("winTitle"))) + pBitmap := Gdip_CloneBitmapArea(pBitmapW, cropX, cropY, cropW, cropH) + + Gdip_DisposeImage(pBitmapW) + Gdip_SaveBitmapToFile(pBitmap, filePath) - pack := "" - for _, packName in packNames { - if (pack != "") - pack .= ", " - pack .= packName + ; Don't dispose pBitmap if it's a PACKSTATS screenshot + if (filename != "PACKSTATS") { + Gdip_DisposeImage(pBitmap) + return filePath } - return { cards: cards, pack: pack, rarity: rarity, shinedust: shinedust, raw: raw_msg, pulls: pulls } + ; For PACKSTATS, return both values and delete temp file after OCR is done + return {filepath: filePath, bitmap: pBitmap, deleteAfterUse: true} } -; Wait for the Helper to produce a parseable result.rc containing the cards, -; then read and parse it. Does not terminate the watcher (caller must do that -; once the result has been consumed). -ReadEliteDeckResult(timeoutSec := 45) { - prof := Prof_Scope(A_ThisFunc) - global session +; Pause Script +PauseScript: + CreateStatusMessage("Pausing...",,,, false) + g_scriptPaused := true + Pause, On, 1 +return - adbCommand := session.get("adbPath") . " -s 127.0.0.1:" . session.get("adbPort") +; Resume Script +ResumeScript: + CreateStatusMessage("Resuming...",,,, false) + session.set("StartSkipTime", A_TickCount) ;reset stuck timers session.set("failSafe", A_TickCount) - Loop { - output := GetStdout(adbCommand . " shell cat /data/ptcgp/result.rc") - result := ParsePackResultOutput(output) - if (result) - return result + g_scriptPaused := false + Pause, Off +return - Sleep, 500 - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for Elite Deck result...`n(" . failSafeTime . "/" . timeoutSec . " seconds)") - if (failSafeTime >= timeoutSec) - break +TogglePauseScript: + if (g_scriptPaused) { + CreateStatusMessage("Resuming...",,,, false) + session.set("StartSkipTime", A_TickCount) ;reset stuck timers + session.set("failSafe", A_TickCount) + g_scriptPaused := false + Pause, Off + } else { + CreateStatusMessage("Pausing...",,,, false) + g_scriptPaused := true + Pause, On, 1 } - return false -} +return -UpdatePackCountAfterOpening(defaultOpenedPacks := 1) { - global session +; Stop Script +StopScript: + ToggleStop() +return - expectedOpenedPacks := session.get("expectedPackOpenCount") + 0 - if (expectedOpenedPacks >= 1 && expectedOpenedPacks <= 10) - defaultOpenedPacks := expectedOpenedPacks - session.set("expectedPackOpenCount", 1) +DevMode: + ToggleDevMode() +return - oldPackCount := session.get("accountOpenPacks") + 0 - openedPackCount := defaultOpenedPacks - newPackCount := EvaluatePackCount() +ReloadScript: + CleanupBeforeExit() + SafeReload("Toolbar reload") +return - if (newPackCount = 0) { - session.set("accountOpenPacks", oldPackCount + defaultOpenedPacks) - } else { - packDelta := newPackCount - oldPackCount - if (packDelta >= 1 && packDelta <= 10) - openedPackCount := packDelta +TestScript: + ToggleTestScript() +return - session.set("accountOpenPacks", newPackCount) +; === Single instance stop handlers (GUI button) === +StopImmediatelySingle: + targetHwnd := session.get("RememberStopPreferenceSingleHwnd") + GuiControlGet, RememberStopPreferenceSingle, , %targetHwnd% + if (RememberStopPreferenceSingle) { + botConfig.set("stopPreferenceSingle", "immediate", "Extra") + botConfig.saveConfigToSettings("Extra") + } + Gui, StopConfirm:Destroy + CleanupBeforeExit() +ExitApp +return - if (session.get("injectMethod") && session.get("loadedAccount") && session.get("accountFileName") != "") { - accountMeta := AccountMetadata_Get(session.get("scriptName"), session.get("accountFileName"), session.get("loadedAccount")) - accountMeta["deviceAccount"] := GetCurrentDeviceAccountForMetadata() - accountMeta["packCount"] := newPackCount - AccountMetadata_SaveAccount(session.get("scriptName"), session.get("accountFileName"), accountMeta) - } +StopWaitEndSingle: + Gui, StopConfirm:Submit, NoHide + GuiControlGet, RememberStopPreferenceSingle, , ui_RememberStopPreferenceSingle + if (RememberStopPreferenceSingle) { + botConfig.set("stopPreferenceSingle", "wait_end", "Extra") + botConfig.saveConfigToSettings("Extra") } + Gui, StopConfirm:Destroy + session.set("stopToggle", true) + CreateStatusMessage("Stopping script at the end of the run...",,,, false) +return - if (session.get("injectMethod") && session.get("loadedAccount")) - UpdateAccount() +StopConfirmGuiClose: +StopConfirmGuiEscape: + Gui, StopConfirm:Destroy +return - session.set("packsInPool", session.get("packsInPool") + openedPackCount) - session.set("packsThisRun", session.get("packsThisRun") + openedPackCount) - - return openedPackCount -} - -EvaluatePack() { - prof := Prof_Scope(A_ThisFunc) - global session - adbCommand := session.get("adbPath") . " -s 127.0.0.1:" . session.get("adbPort") - expectedOpenedPacks := session.get("expectedPackOpenCount") + 0 - maxAttempts := expectedOpenedPacks > 1 ? 100 : 10 - foundResult := false - Loop, %maxAttempts% { - RunWait, % adbCommand . " shell test -f /data/ptcgp/result.rc", , Hide - if (ErrorLevel = 0) { - foundResult := true - break - } - Sleep, 300 +; === All instances stop handlers (Shift+F7 from instance 1) === +StopImmediatelyAll: + targetHwnd := session.get("RememberStopPreferenceHwnd") + GuiControlGet, RememberStopPreference, , %targetHwnd% + if (RememberStopPreference) { + botConfig.set("stopPreference", "immediate", "Extra") + botConfig.saveConfigToSettings("Extra") } - if (foundResult && expectedOpenedPacks > 1) - Sleep, 1000 - adbWriteRaw("pkill -f /data/ptcgp/ptcgpb") - waitadb() - - SavePackOpeningMissionUserPrefsSnapshot("post") - - output := GetStdout(adbCommand . " shell cat /data/ptcgp/result.rc") - parsedResult := ParsePackResultOutput(output) - if (!parsedResult && expectedOpenedPacks > 1) - LogDebug("EvaluatePack 10-pack parse failed. result.rc length=" . StrLen(output), "debug_cards.txt") - return parsedResult -} - -EvaluatePackCount() { - prof := Prof_Scope(A_ThisFunc) - - LogTrace("EvaluatePackCount running ptcgpb packcount", "ADB.txt") - return GetPtcgpbPackCount() -} - -RecoverPack() { - prof := Prof_Scope(A_ThisFunc) - global session - adbCommand := session.get("adbPath") . " -s 127.0.0.1:" . session.get("adbPort") - - pre := PackOpeningMissionUserPrefsSnapshotPath("pre") - post := PackOpeningMissionUserPrefsSnapshotPath("post") - - adbWriteRaw("rm -f /data/ptcgp/result.rc") - adbWriteRaw("rm -f /data/ptcgp/result.log") - adbWriteRaw("/data/ptcgp/ptcgpb diff-files --duplicate " . pre . " " . post) - output := GetStdout(adbCommand . " shell cat /data/ptcgp/result.rc") - return ParsePackResultOutput(output) -} - -CardDetection_QueueGodPack(validity, cards := "", finalizeAccount := false) { - global session - - session.set("keepAccount", true) - session.set("foundGP", true) - session.set("pendingGodPack", true) - session.set("pendingGodPackValidity", validity) - session.set("pendingGodPackCards", cards) - session.set("pendingGodPackFinalize", finalizeAccount) - session.set("manualVipValidity", validity) - session.set("pendingGodPackScreenshot", Screenshot(validity)) - LogInfo("Queued " . validity . " God Pack notification until pack cleanup finishes", "GPlog.txt") - return true -} - -CardDetection_HasPendingGodPack() { - global session - return session.get("pendingGodPack") -} - -CardDetection_CheckGodPackDeferred(invalidPack := false, cards := "", finalizeAccount := false) { - global botConfig, session - - currentPackInfo := session.get("currentPackInfo") + Gui, StopConfirmAll:Destroy + StopAllInstances() +return - normalBorders := currentPackInfo["TypeCount"]["normal"] - if (normalBorders) { - logMessage := "Instance: " . session.get("scriptName") " | Not a GP" - LogDebug(logMessage, "debug_cards.txt") - CreateStatusMessage("Not a God Pack...",,,, false) - return false +StopWaitEndAll: + targetHwnd := session.get("RememberStopPreferenceHwnd") + GuiControlGet, RememberStopPreference, , %targetHwnd% + if (RememberStopPreference) { + botConfig.set("stopPreference", "wait_end", "Extra") + botConfig.saveConfigToSettings("Extra") } + Gui, StopConfirmAll:Destroy + ; Signal all other instances to stop after their current run + SignalStopAfterRun() + session.set("stopToggle", true) + CreateStatusMessage("Stopping script at the end of the run...",,,, false) +return - session.set("keepAccount", true) - - if (!invalidPack) { - tempStarCount := currentPackInfo["TypeCount"]["fullart"] + currentPackInfo["TypeCount"]["rainbow"] + currentPackInfo["TypeCount"]["trainer"] - - logMessage := "Instance: " . session.get("scriptName") " | tempStarCount " . tempStarCount - LogDebug(logMessage, "debug_cards.txt") - - requiredStars := botConfig.get("minStars") - if (requiredStars > 0 && tempStarCount < requiredStars) { - CreateStatusMessage("Pack doesn't contain enough 2 stars...",,,, false) - invalidPack := true - } +StopAndKillMuMuAll: + GuiControlGet, RememberStopPreference, , ui_RememberStopPreference + Gui, StopConfirmAll:Submit, NoHide + if (RememberStopPreference) { + settingsPath := A_ScriptDir . "\..\Settings.ini" + IniWrite, kill_mumu, %settingsPath%, UserSettings, stopPreference } + Gui, StopConfirmAll:Destroy + ; Kill ALL MuMu instances before calling StopAllInstances (which does ExitApp) + Loop, % botConfig.get("Instances") { + killInstance(A_Index) + Sleep, 500 + } + Sleep, 1000 + StopAllInstances() +return - return CardDetection_QueueGodPack(invalidPack ? "Invalid" : "Valid", cards, finalizeAccount) -} - -CardDetection_FlushPendingGodPack() { - global session - - if (!CardDetection_HasPendingGodPack()) - return false - - validity := session.get("pendingGodPackValidity") - cards := session.get("pendingGodPackCards") - finalizeAccount := session.get("pendingGodPackFinalize") - preCapturedScreenshot := session.get("pendingGodPackScreenshot") - session.set("pendingGodPack", false) - session.set("pendingGodPackValidity", "") - session.set("pendingGodPackCards", "") - session.set("pendingGodPackFinalize", false) - session.set("pendingGodPackScreenshot", "") - - GodPackFound(validity, cards, true, preCapturedScreenshot) +StopConfirmAllGuiClose: +StopConfirmAllGuiEscape: + Gui, StopConfirmAll:Destroy +return - if (validity = "Invalid") - RemoveFriends() - IniWrite, 0, % session.get("scriptIniFile"), UserSettings, DeadCheck +; ===== TIMER FUNCTIONS ===== +RefreshAccountLists: + createAccountList(session.get("scriptName")) +Return - if (finalizeAccount && validity != "Invalid") - FinalizeInjectedGodPackAccount() +CleanupUsedAccountsTimer: + CleanupUsedAccounts() +Return - reason := (validity = "Invalid") ? "Invalid God Pack Found." : "God Pack found. Continuing..." - restartGameInstance(reason, "GodPack") - return true -} +LiveMetricsTimer: + updateTotalTime() + session.set("VRAMUsage", GetVRAMByScriptName(session.get("scriptName"))) + CreateStatusMessage(generateStatusText(), "AvgRuns", 0, 605, false, true) +Return -CheckPack(stopEarly := false) { - prof := Prof_Scope(A_ThisFunc) - expectedOpenedPacks := session.get("expectedPackOpenCount") + 0 +; ===== HOTKEYS ===== +~+F5:: + CleanupBeforeExit() + SafeReload("Shift+F5") +return +~+F6:: + Gosub, TogglePauseScript +return +~+F7:: + ; Only instance 1 handles Shift+F7 - shows popup and controls all instances + ; Other instances do nothing here; they receive commands via PostMessage from instance 1 + if (session.get("scriptName") = "1") { + ToggleStopAll() + } +return +~+F8::ToggleDevMode() +;~F9::restartGameInstance("F9") - result := EvaluatePack() - if (!result) { - LogWarn("Failed at 1", "debug_cards.txt") - result := RecoverPack() +; ===== RECORDER HOTKEY ===== +; Fires on every left-click; no-op unless recording is active. +/* +~+LButton:: + if (!rec_Active) { + return } - if (!result) { - LogWarn("Failed at 2", "debug_cards.txt") - ReportPackRecognitionFailure() - if (!stopEarly && expectedOpenedPacks > 1) { - UpdatePackCountAfterOpening() - } - if (!stopEarly && expectedOpenedPacks <= 1) { - CheckPackFallback() - } + if (rec_SuspendCapture) { return } - cards := result.cards - pack := result.pack - rarity := result.rarity - shinedust := result.shinedust - raw_msg := result.raw - if(rarity[1] = 0) { - LogWarn(raw_msg, "debug_cards.txt") - ; Fallback in case recognition failed - ReportPackRecognitionFailure() - if (!stopEarly && expectedOpenedPacks > 1) { - UpdatePackCountAfterOpening() - } - if (!stopEarly && expectedOpenedPacks <= 1) { - CheckPackFallback() - } + MouseGetPos, rec_DownX, rec_DownY, clickedHwnd + if (clickedHwnd != WinExist(session.get("winTitle"))) { return } + CoordMode, Mouse, Screen + MouseGetPos, rec_DownX, rec_DownY + CoordMode, Mouse, Relative + + rec_DownTime := A_TickCount + KeyWait, LButton - ; store result - LogToCardDatabase(result) + CoordMode, Mouse, Screen + MouseGetPos, upX, upY + CoordMode, Mouse, Relative + held := A_TickCount - rec_DownTime - logMessage := "Instance: " . session.get("scriptName") " | Stored to card database" - LogDebug(logMessage, "debug_cards.txt") - UpdatePackCountAfterOpening() - AddShinedustToDatabase(shinedust) + ; Compute window-relative coords first, then use them for distance + WinGetPos, wx, wy, ww, wh, % session.get("winTitle") + devX1 := rec_DownX - wx + devY1 := rec_DownY - wy + devX2 := upX - wx + devY2 := upY - wy - if (stopEarly) { - if (botConfig.get("s4tEnabled")) { - CheckCardsSimple(result) - } + ; Ignore clicks that started outside the emulator window + if (devX1 < 0 || devY1 < 0 || devX1 > ww || devY1 > wh) { return } - ; NEW: Disable card detection for Create Bots and Inject 13P+ - ; Only run detection for Inject Wonderpick 96P+ - skipCardDetection := (botConfig.get("deleteMethod") = "Create Bots (13P)" || botConfig.get("deleteMethod") = "Inject 13P+") - multiPullResult := (IsObject(result.pulls) && result.pulls.Length() > 1) || cards.MaxIndex() > 6 - - logMessage := "Instance: " . session.get("scriptName") " | Skip Card Detection: " . skipCardDetection - LogDebug(logMessage, "debug_cards.txt") - - ; If not doing card detection and no friends and s4t disabled, just return early - if(skipCardDetection && !session.get("friendIDs") && botConfig.get("FriendID") = "" && !botConfig.get("s4tEnabled")) - return false - - currentPackIs4Card := cards.MaxIndex() = 4 - currentPackIs6Card := cards.MaxIndex() = 6 + dist := Sqrt((devX2 - devX1) ** 2 + (devY2 - devY1) ** 2) - totalCardsInPack := cards.MaxIndex() + if (dist > 20) + type := "swipe" + else if (held >= 500) + type := "hold" + else + type := "click" - if (!multiPullResult) { - ; Wait for cards to render before checking. - Loop { - if (CheckCardLoading(totalCardsInPack) = 0) - break - Delay(1) + ssPath := RecordingCapture() + rawDelay := A_TickCount - rec_LastTime - held + delay := (rawDelay > 0) ? rawDelay // Delay : 0 + rec_LastTime := A_TickCount + actionIdx := rec_Actions.Length() + 1 + LogDebug("[Hook] Pushing action idx=" actionIdx " type=" type " x1=" devX1 " y1=" devY1 " x2=" devX2 " y2=" devY2 " ssPath=" ssPath, "recorder.txt") + rec_Actions.Push({type: type, x1: devX1, y1: devY1, x2: devX2, y2: devY2 + , duration: held, delay: delay, screenshot: ssPath, comment: "", code: ""}) + LogDebug("[Hook] rec_Actions.Length()=" rec_Actions.Length() " last.screenshot=" rec_Actions[rec_Actions.Length()].screenshot, "recorder.txt") + CreateStatusMessage("Recording: Capture`n" type " (" devX1 "," devY1 ")-(" devX2 "," devY2 ") dist=" dist) +return +*/ +screenshotscript: + if (rec_Active) { + rawDelay := A_TickCount - rec_LastTime + delay := (rawDelay > 0) ? rawDelay // botConfig.get("Delay") : 0 + rec_SuspendCapture := true + CreateStatusMessage("Recording: Capture`nYou could deside what to do with it later.") + rec_LastScreenGrab := "" + Screenshot_dev() + rec_SuspendCapture := false + if (rec_LastScreenGrab != "") { + rec_Actions.Push({type: "screenshot" + , fileName: rec_LastScreenGrab.fileName + , needlePath: rec_LastScreenGrab.needlePath + , screenshot: rec_LastScreenGrab.screenshot + , x1: rec_LastScreenGrab.x1, y1: rec_LastScreenGrab.y1 + , x2: rec_LastScreenGrab.x2, y2: rec_LastScreenGrab.y2 + , x3: rec_LastScreenGrab.x3, y3: rec_LastScreenGrab.y3 + , delay: delay, screenshot: rec_LastScreenGrab.needlePath + , comment: "", code: "", choice: ""}) + rec_LastTime := A_TickCount + CreateStatusMessage("Recording: Image captured") + } else { + CreateStatusMessage("") } - Delay(1) + } else { + Screenshot_dev() } +return - found1Dmnd := CountOccurances(cards, rarity, 1) - found2Dmnd := CountOccurances(cards, rarity, 2) - found3Dmnd := CountOccurances(cards, rarity, 3) - found4Dmnd := CountOccurances(cards, rarity, 4) - found1Star := CountOccurances(cards, rarity, 7) - foundTrainer := CountOccurances(cards, rarity, 5, "TR_") - foundFullArt := CountOccurances(cards, rarity, 5, "PK_") - foundRainbow := CountOccurances(cards, rarity, 8) - foundImmersive := CountOccurances(cards, rarity, 9) - foundCrown := CountOccurances(cards, rarity, 10) - foundShiny1Star := CountOccurances(cards, rarity, 11) - foundShiny2Star := CountOccurances(cards, rarity, 12) - - PackMethod_UpdateSkipFriendRenewFromCounts(foundImmersive, foundCrown, foundShiny1Star, foundShiny2Star) - - foundWishlist := Wishlist_ProcessPack(cards, pack) - foundWishlist2Star := Wishlist_CountTwoStarMatches(cards, rarity, session.get("wishlistMap")) - - logMessage := "Instance: " . session.get("scriptName") " | Found: " . found1Dmnd - logMessage := logMessage . "|" . found2Dmnd - logMessage := logMessage . "|" . found2Dmnd - logMessage := logMessage . "|" . found3Dmnd - logMessage := logMessage . "|" . found4Dmnd - logMessage := logMessage . "|" . found1Star - logMessage := logMessage . "|" . foundTrainer - logMessage := logMessage . "|" . foundFullArt - logMessage := logMessage . "|" . foundRainbow - logMessage := logMessage . "|" . foundImmersive - logMessage := logMessage . "|" . foundCrown - logMessage := logMessage . "|" . foundShiny1Star - logMessage := logMessage . "|" . foundShiny2Star - LogDebug(logMessage, "debug_cards.txt") - - if (botConfig.get("s4tEnabled")) { - tradeableList := [] - tradeableList.Push({key: "3Diamond", flag: botConfig.get("s4t3Dmnd"), count: found3Dmnd}) - tradeableList.Push({key: "4Diamond", flag: botConfig.get("s4t4Dmnd"), count: found4Dmnd}) - tradeableList.Push({key: "1Star", flag: botConfig.get("s4t1Star"), count: found1Star}) - tradeableList.Push({key: "Trainer", flag: botConfig.get("s4tTrainer"), count: foundTrainer}) - tradeableList.Push({key: "FullArt", flag: botConfig.get("s4tFullArt"), count: foundFullArt}) - tradeableList.Push({key: "Rainbow", flag: botConfig.get("s4tRainbow"), count: foundRainbow}) - tradeableList.Push({key: "Immersive", flag: botConfig.get("s4tImmersive"), count: foundImmersive}) - tradeableList.Push({key: "Crown", flag: botConfig.get("s4tCrown"), count: foundCrown}) - tradeableList.Push({key: "Shiny1Star",flag: botConfig.get("s4tShiny1Star"), count: foundShiny1Star}) - tradeableList.Push({key: "Shiny2Star",flag: botConfig.get("s4tShiny2Star"), count: foundShiny2Star}) - tradeableList.Push({key: "Wishlist", flag: botConfig.get("s4tWishlist"), count: foundWishlist}) - - foundCards := {} - foundTradeable := 0 - for _, item in tradeableList { - foundCards[item.key] := 0 - if (item.flag) { - foundTradeable += item.count - foundCards[item.key] := item.count - } - } +Logout: + closePTCGPApp() + adbWriteRaw("rm /data/data/jp.pokemon.pokemontcgp/shared_prefs/deviceAccount:.xml") + AccountMetadata_CloseTempForInstance(session.get("scriptName")) + startPTCGPApp() +return - logMessage := "Instance: " . session.get("scriptName") " | S4T Trandables: " . foundTradeable - LogDebug(logMessage, "debug_cards.txt") +bboxScript: + ToggleBBox() +return - if (foundTradeable > 0) { - FoundTradeableNew(foundCards, pack, cards, rarity, multiPullResult) - ; Continue with the rest of the run in s4t mode; don't return early. - } - } +bboxNpauseScript: + TogglebboxNpause() +return - if (multiPullResult) { - return false - } +bboxDraw2(X1, Y1, X2, Y2, color) { + global session - ; Skip rest of card detection if this is Create Bots or Inject 13P+ - if (skipCardDetection) { - return false - } + WinGetPos, xwin, ywin, Width, Height, % session.get("winTitle") + BoxWidth := 10 + BoxHeight := 10 + Xm1:=X1-(BoxWidth/2) + Xm2:=X2-(BoxWidth/2) + Ym1:=Y1-(BoxWidth/2) + Ym2:=Y2-(BoxWidth/2) + Xh1:=Xm1+BoxWidth + Xh2:=Xm2+BoxWidth + Yh1:=Ym1+BoxHeight + Yh2:=Ym2+BoxHeight - logMessage := "Instance: " . session.get("scriptName") " | GP Check " - LogDebug(logMessage, "debug_cards.txt") - - foundLabel := false - - ; Check if the current pack is valid (for Inject Wonderpick 96P+ only now) - foundShiny := foundShiny1Star + foundShiny2Star - foundInvalid := foundShiny + foundCrown + foundImmersive - tempStarCount := foundFullArt + foundRainbow + foundTrainer - normalBorders := found1Dmnd + found2Dmnd + found3Dmnd + found4Dmnd - - logMessage := "Instance: " . session.get("scriptName") " | normalBorders: " . normalBorders - LogDebug(logMessage, "debug_cards.txt") - - ; Build currentPackInfo in session so GodPackFound can read starCount correctly - synthPackInfo := {"isVerified": true, "CardSlot": [], "TypeCount": {}} - synthPackInfo["TypeCount"]["normal"] := normalBorders - synthPackInfo["TypeCount"]["1star"] := found1Star - synthPackInfo["TypeCount"]["3diamond"] := found3Dmnd - synthPackInfo["TypeCount"]["trainer"] := foundTrainer - synthPackInfo["TypeCount"]["fullart"] := foundFullArt - synthPackInfo["TypeCount"]["rainbow"] := foundRainbow - synthPackInfo["TypeCount"]["immersive"] := foundImmersive - synthPackInfo["TypeCount"]["crown"] := foundCrown - session.set("currentPackInfo", synthPackInfo) - - if (foundInvalid && botConfig.get("InvalidCheck")) { - logMessage := "Instance: " . session.get("scriptName") " | Invalid" - LogDebug(logMessage, "debug_cards.txt") - ; Skip invalid packs if invalidcheck is active - return - } + ; Create a GUI + guiSuffix := session.get("winTitle") + Gui, BoundingBox%guiSuffix%:+AlwaysOnTop +ToolWindow -Caption +E0x20 + Gui, BoundingBox%guiSuffix%:Color, 123456 + Gui, BoundingBox%guiSuffix%:+LastFound ; Make the GUI window the last found window for use by the line below. (straght from documentation) + WinSet, TransColor, 123456 ; Makes that specific color transparent in the gui - if (foundInvalid) { - logMessage := "Instance: " . session.get("scriptName") " | Doing Invalid Check" - LogDebug(logMessage, "debug_cards.txt") - ; Pack is invalid... - foundInvalidGP := CardDetection_CheckGodPackDeferred(true, cards, false) ; GP is never ignored + ; Create the borders and show + Gui, BoundingBox%guiSuffix%:Add, Progress, x%Xm1% y%Ym1% w%BoxWidth% h2 %color% + Gui, BoundingBox%guiSuffix%:Add, Progress, x%Xm1% y%Ym1% w2 h%BoxHeight% %color% + Gui, BoundingBox%guiSuffix%:Add, Progress, x%Xh1% y%Ym1% w2 h%BoxHeight% %color% + Gui, BoundingBox%guiSuffix%:Add, Progress, x%Xm1% y%Yh1% w%BoxWidth% h2 %color% - if (foundInvalidGP){ - return - } - if (!foundInvalidGP) { - ; If not a GP and not "ignore invalid packs", check what cards the current pack contains which make it invalid - if (botConfig.get("ShinyCheck") && foundShiny && !foundLabel) - foundLabel := "Shiny" - if (botConfig.get("ImmersiveCheck") && foundImmersive && !foundLabel) - foundLabel := "Immersive" - if (botConfig.get("CrownCheck") && foundCrown && !foundLabel) - foundLabel := "Crown" - - ; Report invalid cards found. - if (foundLabel) { - FoundStars(foundLabel) - restartGameInstance(foundLabel . " found. Continuing...", "GodPack") - } - } - - IniWrite, 0, % session.get("scriptIniFile"), UserSettings, DeadCheck - return - } - - ; Check for god pack. if found we know its not invalid - session.set("foundGP", CardDetection_CheckGodPackDeferred(false, cards, true)) - - if (session.get("foundGP")) { - return - } - - ; Check for 2-star cards (for Inject Wonderpick 96P+ only) - 2starCount := false - - if (botConfig.get("PseudoGodPack") && !foundLabel) { - 2starCount := foundTrainer + foundRainbow + foundFullArt - if (2starCount > 1) - foundLabel := "Double two star" - } - if (botConfig.get("TrainerCheck") && !foundLabel && foundTrainer) { - foundLabel := "Trainer" - } - if (botConfig.get("RainbowCheck") && !foundLabel && foundRainbow) { - foundLabel := "Rainbow" - } - if (botConfig.get("FullArtCheck") && !foundLabel && foundFullArt) { - foundLabel := "Full Art" - } - if (botConfig.get("WishlistCheck") && !foundLabel && foundWishlist2Star) { - session.set("wishlistMatches", Wishlist_TwoStarMatchEntries(cards, rarity, session.get("wishlistMap"))) - foundLabel := "Wishlist" - } - - if (foundLabel) { - FinalizeInjectedGodPackAccount() - FoundStars(foundLabel, cards) - restartGameInstance(foundLabel . " found. Continuing...", "GodPack") - } - -} - -CheckPackFallback() { - prof := Prof_Scope(A_ThisFunc) - global botConfig, session - - currentPackIs6Card := false ; reset before each pack check - currentPackIs6Card := false ; reset before each pack check - - UpdatePackCountAfterOpening() - - ; NEW: Disable card detection for Create Bots and Inject 13P+ - ; Only run detection for Inject Wonderpick 96P+ - skipCardDetection := (botConfig.get("deleteMethod") = "Create Bots (13P)" || botConfig.get("deleteMethod") = "Inject 13P+") - - ; If not doing card detection and no friends and s4t disabled, just return early - if(skipCardDetection && !session.get("friendIDs") && botConfig.get("FriendID") = "" && !botConfig.get("s4tEnabled")) - return false - - currentPackIs4Card := DetectFourCardPack() - if (!currentPackIs4Card) { - currentPackIs6Card := DetectSixCardPack() - } - - ; Determine total cards in pack for 4-diamond s4t calculations - totalCardsInPack := currentPackIs6Card ? 6 : (currentPackIs4Card ? 4 : 5) - - ; Wait for cards to render before checking. - Loop { - if (CheckCardLoading(totalCardsInPack) = 0) - break - Delay(1) - } - Delay(1) - - currentPackInfo := AnalysisBorder(totalCardsInPack) - session.set("currentPackInfo", currentPackInfo) - - ; NEW: Check for s4t tradeable cards FIRST (before invalid/godpack checks) - ; This allows s4t to work even on "invalid" packs with crowns/immersives/etc - if (botConfig.get("s4tEnabled")) { - found3Dmnd := 0 - found4Dmnd := 0 - found1Star := 0 - foundGimmighoul := 0 - foundCrown := 0 - foundImmersive := 0 - foundShiny1Star := 0 - foundShiny2Star := 0 - foundTrainer := 0 - foundRainbow := 0 - foundFullArt := 0 - - ; Check all border types for s4t (only if enabled) - if (botConfig.get("s4t3Dmnd")) { - found3Dmnd := currentPackInfo["TypeCount"]["3diamond"] - } - if (botConfig.get("s4t1Star")) { - found1Star := currentPackInfo["TypeCount"]["1star"] - } - if (botConfig.get("s4t4Dmnd")) { - ; Detecting a 4-diamond EX card by subtracting other types - found4Dmnd := totalCardsInPack - currentPackInfo["TypeCount"]["normal"] - if (found4Dmnd > 0) { - if (botConfig.get("s4t3Dmnd")) - found4Dmnd -= found3Dmnd - else - found4Dmnd -= currentPackInfo["TypeCount"]["3diamond"] - } - if (found4Dmnd > 0) { - if (botConfig.get("s4t1Star")) - found4Dmnd -= found1Star - else - found4Dmnd -= currentPackInfo["TypeCount"]["1star"] - } - if (found4Dmnd > 0) { - found4Dmnd -= currentPackInfo["TypeCount"]["trainer"] - found4Dmnd -= currentPackInfo["TypeCount"]["rainbow"] - found4Dmnd -= currentPackInfo["TypeCount"]["fullart"] - found4Dmnd -= currentPackInfo["TypeCount"]["immersive"] - found4Dmnd -= currentPackInfo["TypeCount"]["crown"] - found4Dmnd -= currentPackInfo["TypeCount"]["ShinyEx"] - found4Dmnd -= currentPackInfo["TypeCount"]["shiny1star"] - } - } - if (botConfig.get("s4tGholdengo") && session.get("openPack") = "Shining") { - foundGimmighoul := FindCard("gimmighoul") - } - - ; NEW: Only check if the specific card type is enabled - if (botConfig.get("s4tCrown")) { - foundCrown := currentPackInfo["TypeCount"]["crown"] - } - if (botConfig.get("s4tImmersive")) { - foundImmersive := currentPackInfo["TypeCount"]["immersive"] - } - if (botConfig.get("s4tShiny2Star")) { - foundShiny2Star := currentPackInfo["TypeCount"]["ShinyEx"] - } - if (botConfig.get("s4tShiny1Star")) { - foundShiny1Star := currentPackInfo["TypeCount"]["shiny1star"] - } - if (botConfig.get("s4tTrainer")) { - foundTrainer := currentPackInfo["TypeCount"]["trainer"] - } - if (botConfig.get("s4tRainbow")) { - foundRainbow := currentPackInfo["TypeCount"]["rainbow"] - } - if (botConfig.get("s4tFullArt")) { - foundFullArt := currentPackInfo["TypeCount"]["fullart"] - } - - foundTradeable := found3Dmnd + found4Dmnd + found1Star + foundGimmighoul + foundCrown + foundImmersive + foundShiny1Star + foundShiny2Star + foundTrainer + foundRainbow + foundFullArt - - if (foundTradeable > 0) { - FoundTradeable(found3Dmnd, found4Dmnd, found1Star, foundGimmighoul, foundCrown, foundImmersive, foundShiny1Star, foundShiny2Star, foundTrainer, foundRainbow, foundFullArt) - ; Continue with the rest of the run in s4t mode; don't return early. - } - } - - ; Skip rest of card detection if this is Create Bots or Inject 13P+ - if (skipCardDetection) { - return false - } - - foundLabel := false - - ; Check if the current pack is valid (for Inject Wonderpick 96P+ only now) - foundShiny := currentPackInfo["TypeCount"]["ShinyEx"] + currentPackInfo["TypeCount"]["shiny1star"] - foundCrown := currentPackInfo["TypeCount"]["crown"] - foundImmersive := currentPackInfo["TypeCount"]["immersive"] - foundInvalid := foundShiny + foundCrown + foundImmersive - - if (foundInvalid) { - ; Pack is invalid... - foundInvalidGP := CardDetection_CheckGodPackDeferred(true, "", false) ; GP is never ignored - - if (foundInvalidGP){ - return - } - if (!foundInvalidGP && !botConfig.get("InvalidCheck")) { - ; If not a GP and not "ignore invalid packs", check what cards the current pack contains which make it invalid - if (botConfig.get("ShinyCheck") && foundShiny && !foundLabel) - foundLabel := "Shiny" - if (botConfig.get("ImmersiveCheck") && foundImmersive && !foundLabel) - foundLabel := "Immersive" - if (botConfig.get("CrownCheck") && foundCrown && !foundLabel) - foundLabel := "Crown" - - ; Report invalid cards found. - if (foundLabel) { - FoundStars(foundLabel) - restartGameInstance(foundLabel . " found. Continuing...", "GodPack") - } - } - - IniWrite, 0, % session.get("scriptIniFile"), UserSettings, DeadCheck - return - } - - ; Check for god pack. if found we know its not invalid - session.set("foundGP", CardDetection_CheckGodPackDeferred(false, "", true)) - - if (session.get("foundGP")) { - return - } - - ; Check for 2-star cards (for Inject Wonderpick 96P+ only) - foundTrainer := false - foundRainbow := false - foundFullArt := false - 2starCount := false - - if (botConfig.get("PseudoGodPack") && !foundLabel) { - foundTrainer := currentPackInfo["TypeCount"]["trainer"] - foundRainbow := currentPackInfo["TypeCount"]["rainbow"] - foundFullArt := currentPackInfo["TypeCount"]["fullart"] - 2starCount := foundTrainer + foundRainbow + foundFullArt - if (2starCount > 1) - foundLabel := "Double two star" - } - if (botConfig.get("TrainerCheck") && !foundLabel) { - if(!botConfig.get("PseudoGodPack")) - foundTrainer := currentPackInfo["TypeCount"]["trainer"] - if (foundTrainer) - foundLabel := "Trainer" - } - if (botConfig.get("RainbowCheck") && !foundLabel) { - if(!botConfig.get("PseudoGodPack")) - foundRainbow := currentPackInfo["TypeCount"]["rainbow"] - if (foundRainbow) - foundLabel := "Rainbow" - } - if (botConfig.get("FullArtCheck") && !foundLabel) { - if(!botConfig.get("PseudoGodPack")) - foundFullArt := currentPackInfo["TypeCount"]["fullart"] - if (foundFullArt) - foundLabel := "Full Art" - } - - if (foundLabel) { - FinalizeInjectedGodPackAccount() - FoundStars(foundLabel) - restartGameInstance(foundLabel . " found. Continuing...", "GodPack") - } -} - -ControlClick(X, Y) { - global session - ControlClick, x%X% y%Y%, % session.get("winTitle") -} - -Screenshot_dev(fileType := "Dev", subDir := "", srcPath := "") { - prof := Prof_Scope(A_ThisFunc) - global session, rec_Active - SetWorkingDir %A_ScriptDir% ; Ensures the working directory is the script's directory - - ; Define folder and file paths - fileDir := A_ScriptDir "\..\Screenshots\grab" - if !FileExist(fileDir) - FileCreateDir, %fileDir% - if (subDir) { - fileDir .= "\" . subDir - } - if !FileExist(fileDir) - FileCreateDir, %fileDir% - - ; File path for saving the screenshot locally - fileName := A_Now . "_" . session.get("scriptName") . "_" . fileType . ".png" - filePath := fileDir "\" . fileName - - if (srcPath != "" && FileExist(srcPath)) - pBitmapW := Gdip_CreateBitmapFromFile(srcPath) - else - pBitmapW := from_window(getMuMuHwnd(session.get("winTitle"))) - Gdip_SaveBitmapToFile(pBitmapW, filePath) - - sleep 100 - - try { - CoordMode, Mouse, Client - OwnerWND := WinExist(session.get("winTitle")) - buttonWidth := 40 - - guiSuffix := session.get("winTitle") - Gui, DevMode_ss%guiSuffix%:New, +LastFound -DPIScale - Gui, DevMode_ss%guiSuffix%:Add, Picture, x0 y0 w275 h528 hwndhAppScreen, %filePath% - Gui, DevMode_ss%guiSuffix%:Show, w275 h528, % "Screensho" session.get("winTitle") - - GuiControlGet, PicPos, Pos, %hAppScreen% - - sleep 100 - msgbox click on top-left corner and bottom-right corners - - yBias := 40 - - KeyWait, LButton, D - MouseGetPos , X1, Y1, OutputVarWin, OutputVarControl - KeyWait, LButton, U - X1 := X1 - PicPosX - Y1 := Y1 - PicPosY - - ; Return in case of user close the screen - if !WinExist("Screensho" session.get("winTitle")) - return - - KeyWait, LButton, D - MouseGetPos , X2, Y2, OutputVarWin, OutputVarControl - KeyWait, LButton, U - X2 := X2 - PicPosX - Y2 := Y2 - PicPosY - - W:=X2-X1 - H:=Y2-Y1 - - MsgBox, % X1 ", " Y1 " / " X2 ", " Y2 - - pBitmap := Gdip_CloneBitmapArea(pBitmapW, X1, Y1, W, H) - - InputBox, fileName, ,"Enter the name of the needle to save" - - if (fileName = "") { - return "" - } - - fullScreenPath := filePath - fileDir := A_ScriptDir . "\Needles" - filePath := fileDir "\" . fileName . ".png" - Gdip_SaveBitmapToFile(pBitmap, filePath) - - KeyWait, LButton, D - MouseGetPos , X3, Y3, OutputVarWin, OutputVarControl - KeyWait, LButton, U - X3 := X3 - PicPosX - Y3 := Y3 - PicPosY - - global rec_LastScreenGrab - rec_LastScreenGrab := {fileName: fileName, needlePath: filePath - , screenshot: fullScreenPath - , x1: X1, y1: Y1, x2: X2, y2: Y2, x3: X3, y3: Y3} - - ; Convert window coordinates to device/OCR coordinates - ; Device resolution: 540x960, Window resolution: 277x489, Y offset: 44 - OCR_X1 := Round(X1 * 540 / 283) - OCR_Y1 := Round((Y1 - 44) * 960 / 488) - OCR_W := Round(W * 540 / 283) - OCR_H := Round(H * 960 / 488) - OCR_X2 := OCR_X1 + OCR_W - OCR_Y2 := OCR_Y1 + OCR_H - - ; Calculate center point of the box - OCR_X3 := Round(OCR_X1 + OCR_W / 2) - OCR_Y3 := Round(OCR_Y1 + OCR_H / 2) - - MsgBox, - (LTrim - ctrl+C to copy: - FindOrLoseImage(%X1%, %Y1%, %X2%, %Y2%, , "%fileName%", 0, failSafeTime) - FindImageAndClick(%X1%, %Y1%, %X2%, %Y2%, , "%fileName%", %X3%, %Y3%, sleepTime) - adbClick_wbb(%X3%, %Y3%) - OCR coordinates: %OCR_X3%, %OCR_Y3%, %OCR_W%, %OCR_H% - ) -} -catch { - msgbox Failed to create screenshot GUI -} -CoordMode, Pixel, Screen -return filePath -} - -Screenshot(fileType := "Valid", subDir := "", ByRef fileName := "") { - prof := Prof_Scope(A_ThisFunc) - SetWorkingDir %A_ScriptDir% ; Ensures the working directory is the script's directory - - ; Define folder and file paths - fileDir := A_ScriptDir "\..\Screenshots" - if !FileExist(fileDir) - FileCreateDir, %fileDir% - if (subDir) { - fileDir .= "\" . subDir - if !FileExist(fileDir) - FileCreateDir, %fileDir% - } - if (filename = "PACKSTATS") { - fileDir .= "\temp" - if !FileExist(fileDir) - FileCreateDir, %fileDir% - } - - ; File path for saving the screenshot locally - fileName := A_Now . "_" . session.get("scriptName") . "_" . fileType . "_" . session.get("packsInPool") . "_packs.png" - if (filename = "PACKSTATS") - fileName := "packstats_temp.png" - filePath := fileDir "\" . fileName - - yBias := 40 - cropX := 18 - cropY := 170 - cropW := 240 - cropH := 227 - - if (fileType = "FRIENDCODE"){ - cropX := 18 - cropY := 66 - cropW := 240 - cropH := 165 - } - - pBitmapW := from_window(getMuMuHwnd(session.get("winTitle"))) - pBitmap := Gdip_CloneBitmapArea(pBitmapW, cropX, cropY, cropW, cropH) - - Gdip_DisposeImage(pBitmapW) - Gdip_SaveBitmapToFile(pBitmap, filePath) - - ; Don't dispose pBitmap if it's a PACKSTATS screenshot - if (filename != "PACKSTATS") { - Gdip_DisposeImage(pBitmap) - return filePath - } - - ; For PACKSTATS, return both values and delete temp file after OCR is done - return {filepath: filePath, bitmap: pBitmap, deleteAfterUse: true} -} - -; Pause Script -PauseScript: - CreateStatusMessage("Pausing...",,,, false) - g_scriptPaused := true - Pause, On, 1 -return - -; Resume Script -ResumeScript: - CreateStatusMessage("Resuming...",,,, false) - session.set("StartSkipTime", A_TickCount) ;reset stuck timers - session.set("failSafe", A_TickCount) - g_scriptPaused := false - Pause, Off -return - -TogglePauseScript: - if (g_scriptPaused) { - CreateStatusMessage("Resuming...",,,, false) - session.set("StartSkipTime", A_TickCount) ;reset stuck timers - session.set("failSafe", A_TickCount) - g_scriptPaused := false - Pause, Off - } else { - CreateStatusMessage("Pausing...",,,, false) - g_scriptPaused := true - Pause, On, 1 - } -return - -; Stop Script -StopScript: - ToggleStop() -return - -DevMode: - ToggleDevMode() -return - -ReloadScript: - CleanupBeforeExit() - SafeReload("Toolbar reload") -return - -TestScript: - ToggleTestScript() -return - -; ToggleStop - For GUI button clicks (stops only THIS instance) -ToggleStop() { - global botConfig, session, dictionaryData - - ; Check if user has a saved preference for single instance stop - botConfig.loadIniSectionFromSettingsFile("Extra") - savedStopPreferenceSingle := (botConfig.get("stopPreferenceSingle") = "") ? "none" : botConfig.get("stopPreferenceSingle") - - if (savedStopPreferenceSingle != "none" && savedStopPreferenceSingle != "ERROR" && savedStopPreferenceSingle != "") { - ; Execute the saved preference directly without showing popup - if (savedStopPreferenceSingle = "immediate") { - CleanupBeforeExit() - ExitApp - } else if (savedStopPreferenceSingle = "wait_end") { - session.set("stopToggle", true) - CreateStatusMessage("Stopping script at the end of the run...",,,, false) - } - return - } - - ; Get localized strings - title := dictionaryData[botConfig.get("defaultBotLanguage")]["stop_confirm_title"] - btnImmediate := dictionaryData[botConfig.get("defaultBotLanguage")]["stop_immediately"] - btnWaitEnd := dictionaryData[botConfig.get("defaultBotLanguage")]["stop_wait_end"] - chkRemember := dictionaryData[botConfig.get("defaultBotLanguage")]["stop_remember_preference"] - - ; Create confirmation GUI with checkbox - Gui, StopConfirm:New, +AlwaysOnTop +Owner - Gui, StopConfirm:Add, Text, x20 y15 w260 Center, % title - Gui, StopConfirm:Add, Button, x20 y45 w130 h30 gStopImmediatelySingle, % btnImmediate - Gui, StopConfirm:Add, Button, x160 y45 w130 h30 gStopWaitEndSingle, % btnWaitEnd - Gui, StopConfirm:Add, Checkbox, x20 y85 w260 hwndhRememberStopPreferenceSingle, % chkRemember - Gui, StopConfirm:Show, w310 h115, % title - - session.set("RememberStopPreferenceSingleHwnd", hRememberStopPreferenceSingle) - return -} - -; ToggleStopAll - For Shift+F7 hotkey (stops ALL instances, only called from instance 1) -ToggleStopAll() { - static ui_RememberStopPreference - global botConfig, session, dictionaryData - - ; Check if user has a saved preference - botConfig.loadIniSectionFromSettingsFile("Extra") - savedStopPreference := (botConfig.get("stopPreference") = "") ? "none" : botConfig.get("stopPreference") - - if (savedStopPreference != "none" && savedStopPreference != "ERROR" && savedStopPreference != "") { - ; Execute the saved preference directly without showing popup - if (savedStopPreference = "immediate") { - StopAllInstances() - } else if (savedStopPreference = "wait_end") { - SignalStopAfterRun() - session.set("stopToggle", true) - CreateStatusMessage("Stopping script at the end of the run...",,,, false) - } else if (savedStopPreference = "kill_mumu") { - Loop, % botConfig.get("Instances") { - killInstance(A_Index) - Sleep, 500 - } - Sleep, 1000 - StopAllInstances() - } - return - } - - ; Get localized strings - title := dictionaryData[botConfig.get("defaultBotLanguage")]["stop_confirm_title"] - btnImmediate := dictionaryData[botConfig.get("defaultBotLanguage")]["stop_immediately"] - btnWaitEnd := dictionaryData[botConfig.get("defaultBotLanguage")]["stop_wait_end"] - btnKillMumu := dictionaryData[botConfig.get("defaultBotLanguage")]["stop_kill_mumu"] - chkRemember := dictionaryData[botConfig.get("defaultBotLanguage")]["stop_remember_preference"] - - ; Create confirmation GUI with checkbox - Gui, StopConfirmAll:New, +AlwaysOnTop +Owner - Gui, StopConfirmAll:Add, Text, x20 y15 w400 Center, % title - Gui, StopConfirmAll:Add, Button, x20 y45 w130 h30 gStopImmediatelyAll, % btnImmediate - Gui, StopConfirmAll:Add, Button, x160 y45 w130 h30 gStopWaitEndAll, % btnWaitEnd - Gui, StopConfirmAll:Add, Button, x300 y45 w130 h30 gStopAndKillMuMuAll, % btnKillMumu - Gui, StopConfirmAll:Add, Checkbox, x20 y85 w400 hwndhRememberStopPreference, % chkRemember - Gui, StopConfirmAll:Show, w450 h115, % title - - session.set("RememberStopPreferenceHwnd", hRememberStopPreference) - return -} - -; === Single instance stop handlers (GUI button) === -StopImmediatelySingle: - targetHwnd := session.get("RememberStopPreferenceSingleHwnd") - GuiControlGet, RememberStopPreferenceSingle, , %targetHwnd% - if (RememberStopPreferenceSingle) { - botConfig.set("stopPreferenceSingle", "immediate", "Extra") - botConfig.saveConfigToSettings("Extra") - } - Gui, StopConfirm:Destroy - CleanupBeforeExit() -ExitApp -return - -StopWaitEndSingle: - Gui, StopConfirm:Submit, NoHide - GuiControlGet, RememberStopPreferenceSingle, , ui_RememberStopPreferenceSingle - if (RememberStopPreferenceSingle) { - botConfig.set("stopPreferenceSingle", "wait_end", "Extra") - botConfig.saveConfigToSettings("Extra") - } - Gui, StopConfirm:Destroy - session.set("stopToggle", true) - CreateStatusMessage("Stopping script at the end of the run...",,,, false) -return - -StopConfirmGuiClose: -StopConfirmGuiEscape: - Gui, StopConfirm:Destroy -return - -; === All instances stop handlers (Shift+F7 from instance 1) === -StopImmediatelyAll: - targetHwnd := session.get("RememberStopPreferenceHwnd") - GuiControlGet, RememberStopPreference, , %targetHwnd% - if (RememberStopPreference) { - botConfig.set("stopPreference", "immediate", "Extra") - botConfig.saveConfigToSettings("Extra") - } - Gui, StopConfirmAll:Destroy - StopAllInstances() -return - -StopWaitEndAll: - targetHwnd := session.get("RememberStopPreferenceHwnd") - GuiControlGet, RememberStopPreference, , %targetHwnd% - if (RememberStopPreference) { - botConfig.set("stopPreference", "wait_end", "Extra") - botConfig.saveConfigToSettings("Extra") - } - Gui, StopConfirmAll:Destroy - ; Signal all other instances to stop after their current run - SignalStopAfterRun() - session.set("stopToggle", true) - CreateStatusMessage("Stopping script at the end of the run...",,,, false) -return - -StopAndKillMuMuAll: - GuiControlGet, RememberStopPreference, , ui_RememberStopPreference - Gui, StopConfirmAll:Submit, NoHide - if (RememberStopPreference) { - settingsPath := A_ScriptDir . "\..\Settings.ini" - IniWrite, kill_mumu, %settingsPath%, UserSettings, stopPreference - } - Gui, StopConfirmAll:Destroy - ; Kill ALL MuMu instances before calling StopAllInstances (which does ExitApp) - Loop, % botConfig.get("Instances") { - killInstance(A_Index) - Sleep, 500 - } - Sleep, 1000 - StopAllInstances() -return - -StopConfirmAllGuiClose: -StopConfirmAllGuiEscape: - Gui, StopConfirmAll:Destroy -return - -; Kill all script instances immediately -StopAllInstances() { - global botConfig - - DetectHiddenWindows, On - SetTitleMatchMode, 2 ; Match if title CONTAINS the string (needed for full paths) - - ; Close Main.ahk first - WinClose, Main.ahk ahk_class AutoHotkey - - ; Close all numbered instances (2 through Instances, skip 1 which is us) - Loop, % botConfig.get("Instances") { - if (A_Index != 1) { - WinClose, % A_Index ".ahk ahk_class AutoHotkey" - } - } - - ; Finally exit this instance - CleanupBeforeExit() - ExitApp -} - -OnMonitorWake(wParam, lParam, msg, hwnd) { - global session - - DelayH(3000) - FixInstanceScreen(session.get("scriptName")) -} - -; Message handler for "stop after run" signal from instance 1 -OnStopAfterRunMessage(wParam, lParam, msg, hwnd) { - global session - session.set("stopToggle", true) - CreateStatusMessage("Stopping script at the end of the run...",,,, false) - return 0 -} - -; Send "stop after run" message to all other script instances -SignalStopAfterRun() { - global botConfig - - DetectHiddenWindows, On - SetTitleMatchMode, 2 - - ; Send message to all numbered instances (2 through Instances) - Loop, % botConfig.get("Instances") { - if (A_Index != 1) { - ; Find the window for this instance - WinGet, targetHwnd, ID, % A_Index ".ahk ahk_class AutoHotkey" - if (targetHwnd) { - ; Send custom message (0x500) to signal "stop after run" - PostMessage, 0x500, 0, 0,, ahk_id %targetHwnd% - } - } - } -} - -ToggleTestScript() { - global session - - if(!session.get("GPTest")) { - CreateStatusMessage("In GP Test Mode",,,, false) - session.set("GPTest", true) - } - else { - CreateStatusMessage("Exiting GP Test Mode",,,, false) - session.set("GPTest", false) - } -} - -; ===== TIMER FUNCTIONS ===== -RefreshAccountLists: - createAccountList(session.get("scriptName")) -Return - -CleanupUsedAccountsTimer: - CleanupUsedAccounts() -Return - -LiveMetricsTimer: - updateTotalTime() - session.set("VRAMUsage", GetVRAMByScriptName(session.get("scriptName"))) - CreateStatusMessage(generateStatusText(), "AvgRuns", 0, 605, false, true) -Return - -; ===== HOTKEYS ===== -~+F5:: - CleanupBeforeExit() - SafeReload("Shift+F5") -return -~+F6:: - Gosub, TogglePauseScript -return -~+F7:: - ; Only instance 1 handles Shift+F7 - shows popup and controls all instances - ; Other instances do nothing here; they receive commands via PostMessage from instance 1 - if (session.get("scriptName") = "1") { - ToggleStopAll() - } -return -~+F8::ToggleDevMode() -;~F9::restartGameInstance("F9") - -; ===== RECORDER HOTKEY ===== -; Fires on every left-click; no-op unless recording is active. -/* -~+LButton:: - if (!rec_Active) { - return - } - if (rec_SuspendCapture) { - return - } - MouseGetPos, rec_DownX, rec_DownY, clickedHwnd - if (clickedHwnd != WinExist(session.get("winTitle"))) { - return - } - CoordMode, Mouse, Screen - MouseGetPos, rec_DownX, rec_DownY - CoordMode, Mouse, Relative - - rec_DownTime := A_TickCount - KeyWait, LButton - - CoordMode, Mouse, Screen - MouseGetPos, upX, upY - CoordMode, Mouse, Relative - held := A_TickCount - rec_DownTime - - ; Compute window-relative coords first, then use them for distance - WinGetPos, wx, wy, ww, wh, % session.get("winTitle") - devX1 := rec_DownX - wx - devY1 := rec_DownY - wy - devX2 := upX - wx - devY2 := upY - wy - - ; Ignore clicks that started outside the emulator window - if (devX1 < 0 || devY1 < 0 || devX1 > ww || devY1 > wh) { - return - } - - dist := Sqrt((devX2 - devX1) ** 2 + (devY2 - devY1) ** 2) - - if (dist > 20) - type := "swipe" - else if (held >= 500) - type := "hold" - else - type := "click" - - ssPath := RecordingCapture() - rawDelay := A_TickCount - rec_LastTime - held - delay := (rawDelay > 0) ? rawDelay // Delay : 0 - rec_LastTime := A_TickCount - actionIdx := rec_Actions.Length() + 1 - LogDebug("[Hook] Pushing action idx=" actionIdx " type=" type " x1=" devX1 " y1=" devY1 " x2=" devX2 " y2=" devY2 " ssPath=" ssPath, "recorder.txt") - rec_Actions.Push({type: type, x1: devX1, y1: devY1, x2: devX2, y2: devY2 - , duration: held, delay: delay, screenshot: ssPath, comment: "", code: ""}) - LogDebug("[Hook] rec_Actions.Length()=" rec_Actions.Length() " last.screenshot=" rec_Actions[rec_Actions.Length()].screenshot, "recorder.txt") - CreateStatusMessage("Recording: Capture`n" type " (" devX1 "," devY1 ")-(" devX2 "," devY2 ") dist=" dist) -return -*/ -ToggleDevMode() { - global session - - try { - OwnerWND := getMuMuHwnd(session.get("winTitle")) - if (!OwnerWND) { - CreateStatusMessage("Failed to create button GUI. Emulator window not found.",,,, false) - return - } - - WinGetPos, x, y, Width, Height, ahk_id %OwnerWND% - x4 := x + 5 - y4 := y + 44 - buttonWidth := 40 - - guiSuffix := session.get("winTitle") - Gui, DevMode%guiID%:Destroy - Gui, DevMode%guiID%:New, +Owner%OwnerWND% +LastFound - Gui, DevMode%guiID%:Font, s5 cGray Norm Bold, Segoe UI ; Normal font for input labels - Gui, DevMode%guiID%:Add, Button, % "x" . (buttonWidth * 0) . " y0 w" . buttonWidth . " h25 gbboxScript", bound box - - Gui, DevMode%guiID%:Add, Button, % "x" . (buttonWidth * 1) . " y0 w" . buttonWidth . " h25 gbboxNpauseScript", bbox pause - - Gui, DevMode%guiID%:Add, Button, % "x" . (buttonWidth * 2) . " y0 w" . buttonWidth . " h25 gscreenshotscript", screen grab - ;Gui, DevMode%guiID%:Add, Button, % "x" . (buttonWidth * 3) . " y0 w" . buttonWidth . " h25 gStartStopRecording", Start Recording - - Gui, DevMode%guiID%:Add, Button, % "x" . (buttonWidth * 0) . " y" . (25 + 5) " w" . buttonWidth . " h25 gLogout", Logout - - Gui, DevMode%guiID%:Show, x%x4% y%y4% w250 h100, % "Dev Mode" session.get("winTitle") - - } - catch e { - CreateStatusMessage("Failed to create button GUI. " . e.Message,,,, false) - } -} - -screenshotscript: - if (rec_Active) { - rawDelay := A_TickCount - rec_LastTime - delay := (rawDelay > 0) ? rawDelay // botConfig.get("Delay") : 0 - rec_SuspendCapture := true - CreateStatusMessage("Recording: Capture`nYou could deside what to do with it later.") - rec_LastScreenGrab := "" - Screenshot_dev() - rec_SuspendCapture := false - if (rec_LastScreenGrab != "") { - rec_Actions.Push({type: "screenshot" - , fileName: rec_LastScreenGrab.fileName - , needlePath: rec_LastScreenGrab.needlePath - , screenshot: rec_LastScreenGrab.screenshot - , x1: rec_LastScreenGrab.x1, y1: rec_LastScreenGrab.y1 - , x2: rec_LastScreenGrab.x2, y2: rec_LastScreenGrab.y2 - , x3: rec_LastScreenGrab.x3, y3: rec_LastScreenGrab.y3 - , delay: delay, screenshot: rec_LastScreenGrab.needlePath - , comment: "", code: "", choice: ""}) - rec_LastTime := A_TickCount - CreateStatusMessage("Recording: Image captured") - } else { - CreateStatusMessage("") - } - } else { - Screenshot_dev() - } -return - -Logout: - closePTCGPApp() - adbWriteRaw("rm /data/data/jp.pokemon.pokemontcgp/shared_prefs/deviceAccount:.xml") - AccountMetadata_CloseTempForInstance(session.get("scriptName")) - startPTCGPApp() -return - -bboxScript: - ToggleBBox() -return - -ToggleBBox() { - session.set("dbg_bbox", !session.get("dbg_bbox")) -} - -bboxNpauseScript: - TogglebboxNpause() -return - -TogglebboxNpause() { - session.set("dbg_bboxNpause", !session.get("dbg_bboxNpause")) -} - -bboxDraw2(X1, Y1, X2, Y2, color) { - global session - - WinGetPos, xwin, ywin, Width, Height, % session.get("winTitle") - BoxWidth := 10 - BoxHeight := 10 - Xm1:=X1-(BoxWidth/2) - Xm2:=X2-(BoxWidth/2) - Ym1:=Y1-(BoxWidth/2) - Ym2:=Y2-(BoxWidth/2) - Xh1:=Xm1+BoxWidth - Xh2:=Xm2+BoxWidth - Yh1:=Ym1+BoxHeight - Yh2:=Ym2+BoxHeight - - ; Create a GUI - guiSuffix := session.get("winTitle") - Gui, BoundingBox%guiSuffix%:+AlwaysOnTop +ToolWindow -Caption +E0x20 - Gui, BoundingBox%guiSuffix%:Color, 123456 - Gui, BoundingBox%guiSuffix%:+LastFound ; Make the GUI window the last found window for use by the line below. (straght from documentation) - WinSet, TransColor, 123456 ; Makes that specific color transparent in the gui - - ; Create the borders and show - Gui, BoundingBox%guiSuffix%:Add, Progress, x%Xm1% y%Ym1% w%BoxWidth% h2 %color% - Gui, BoundingBox%guiSuffix%:Add, Progress, x%Xm1% y%Ym1% w2 h%BoxHeight% %color% - Gui, BoundingBox%guiSuffix%:Add, Progress, x%Xh1% y%Ym1% w2 h%BoxHeight% %color% - Gui, BoundingBox%guiSuffix%:Add, Progress, x%Xm1% y%Yh1% w%BoxWidth% h2 %color% - - ; Create the borders and show - Gui, BoundingBox%guiSuffix%:Add, Progress, x%Xm2% y%Ym2% w%BoxWidth% h2 %color% - Gui, BoundingBox%guiSuffix%:Add, Progress, x%Xm2% y%Ym2% w2 h%BoxHeight% %color% - Gui, BoundingBox%guiSuffix%:Add, Progress, x%Xh2% y%Ym2% w2 h%BoxHeight% %color% - Gui, BoundingBox%guiSuffix%:Add, Progress, x%Xm2% y%Yh2% w%BoxWidth% h2 %color% - - xshow := xwin - yshow := ywin - Gui, BoundingBox%guiSuffix%:Show, x%xshow% y%yshow% NoActivate - Sleep, 100 - -} - -adbSwipe_wbb(params) { - prof := Prof_Scope(A_ThisFunc) - global session - - if(session.get("dbg_bbox")) - bboxAndPause_swipe(params, session.get("dbg_bboxNpause")) - adbSwipe(params) -} - -bboxAndPause_swipe(params, doPause := False) { - global session - - guiSuffix := session.get("winTitle") - paramsplit := StrSplit(params , " ") - X1:=round(paramsplit[1] / 535 * 283) - Y1:=round((paramsplit[2] / 960 * 488) + 40) - X2:=round(paramsplit[3] / 535 * 283) - Y2:=round((paramsplit[4] / 960 * 488) + 40) - speed:=paramsplit[5] - CreateStatusMessage("Swiping (" . X1 . "," . Y1 . ") to (" . X2 . "," . Y2 . ") speed " . speed,,,, false) - - color := "BackgroundYellow" - - ;bboxDraw2(X1, Y1, X2, Y2, color) - - bboxDraw(X1-5, Y1-5, X1+5, Y1+5, color) - if (doPause) { - Pause - } - Gui, BoundingBox%guiSuffix%:Destroy - - bboxDraw(X2-5, Y2-5, X2+5, Y2+5, color) - if (doPause) { - Pause - } - Gui, BoundingBox%guiSuffix%:Destroy -} - -Gdip_ImageSearch_wbb(pBitmapHaystack,pNeedle,ByRef OutputList="" - ,OuterX1=0,OuterY1=0,OuterX2=0,OuterY2=0,Variation=0,Trans="" - ,SearchDirection=1,Instances=1,LineDelim="`n",CoordDelim=",") { - prof := Prof_Scope(A_ThisFunc) - profNeedle := Prof_Scope(A_ThisFunc . ":" . pNeedle.Name) - global session - - bias := MuMuBias() - - vret := Gdip_ImageSearch(pBitmapHaystack,pNeedle.needle,OutputList,OuterX1,OuterY1+bias,OuterX2,OuterY2+bias,Variation,Trans,SearchDirection,Instances,LineDelim,CoordDelim) - if(session.get("dbg_bbox")) - bboxAndPause_immage(OuterX1, OuterY1+bias, OuterX2, OuterY2+bias, pNeedle, vret, session.get("dbg_bboxNpause")) - return vret -} - -GetNeedle(Path) { - prof := Prof_Scope(A_ThisFunc) - static NeedleBitmaps := Object() - - if (NeedleBitmaps.HasKey(Path)) { - return NeedleBitmaps[Path] - } else { - pNeedle := Gdip_CreateBitmapFromFile(Path) - needleObj := Object() - needleObj.Path := Path - pathsplit := StrSplit(Path , "\") - needleObj.Name := pathsplit[pathsplit.MaxIndex()] - needleObj.needle := pNeedle - NeedleBitmaps[Path] := needleObj - return needleObj - } -} - -DoTutorial() { - prof := Prof_Scope(A_ThisFunc) - global botConfig, session - - session.set("creationDate", A_Now) - - FindImageAndClick("Create_CountryComboBoxButton", 143, 370) ;select month and year and click - - Delay(3) - adbClick_wbb(80, 400) - Delay(3) - - tutorialLanguage := Trim(session.get("language")) - if (tutorialLanguage = "") { - ; New accounts have no metadata yet, so read the active game's language - if (EnsurePTCGPBHelperInstalled()) { - tutorialLanguage := adbWriteRaw("/data/ptcgp/ptcgpb lang", true) - tutorialLanguage := Trim(StrReplace(tutorialLanguage, "`r"), "`n`t ") - if (RegExMatch(tutorialLanguage, "i)^[a-z][a-z0-9_-]{0,15}$")) - session.set("language", tutorialLanguage) - else - tutorialLanguage := "" - } - } - StringLower, tutorialLanguage, tutorialLanguage - isYearInFront := (tutorialLanguage = "ja" || tutorialLanguage = "ko" || tutorialLanguage = "zh") - if (isYearInFront) { - ; JP/KR display the month picker in the opposite order. - Random, monthScrollCount, 4, 7 - Loop, %monthScrollCount% { - adbSwipe_wbb("100 707 100 903 150") - Delay(3) - } - } - - adbClick_wbb(80, 375) - Delay(3) - session.set("failSafe", A_TickCount) - failSafeTime := 0 - - Loop { - Delay(3) - if(FindImageAndClick("Create_SelectedYear", , , , , 1, failSafeTime)) - break - Delay(3) - adbClick_wbb(142, 159) - Delay(3) - adbClick_wbb(80, 400) - Delay(3) - adbClick_wbb(80, 375) - Delay(3) - adbClick_wbb(82, 422) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for Year`n(" . failSafeTime . "/45 seconds)") - } ;select month and year and click - - adbClick_wbb(200, 400) - Delay(3) - - if (!isYearInFront) { - ; Other languages display the year picker in the opposite order. - Random, yearScrollCount, 4, 7 - Loop, %yearScrollCount% { - adbSwipe_wbb("389 707 389 903 150") - Delay(3) - } - } - adbClick_wbb(200, 375) - Delay(3) - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { ;select month and year and click - Delay(3) - if(FindImageAndClick("Create_SelectedMonth", , , , , 1, failSafeTime)) - break - Delay(3) - adbClick_wbb(142, 159) - Delay(3) - adbClick_wbb(142, 159) - Delay(3) - adbClick_wbb(200, 400) - Delay(3) - adbClick_wbb(200, 375) - Delay(3) - adbClick_wbb(142, 159) - Delay(3) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for Month`n(" . failSafeTime . "/45 seconds)") - } ;select month and year and click - - Delay(3) - FindImageAndClick("Create_BirthConfirmCancelButton", 140, 474, , 1000) - - ;wait date confirmation screen while clicking ok - - FindImageAndClick("Create_TosOpenButton", 203, 371, , 1000) ;wait to be at the tos screen while confirming birth - - FindImageAndClick("Create_TosCloseButton", 139, 299, , 1000) ;wait for tos while clicking it - - FindImageAndClick("Create_TosOpenButton", 142, 486, , 1000) ;wait to be at the tos screen and click x - - FindImageAndClick("Common_PopupXButtonInMain", 142, 339, , 1000) ;wait to be at the tos screen - - FindImageAndClick("Create_TosOpenButton", 142, 486, , 1000) ;wait to be at the tos screen, click X - - Delay(3) - adbClick_wbb(261, 374) - - Delay(3) - adbClick_wbb(261, 406) - - Delay(3) - adbClick_wbb(145, 484) - - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - if(FindOrLoseImage("Create_SignupNotification", 0,failSafeTime)) { - ; notification flow - adbClick(138, 434) - Sleep, 1000 - adbClick(136, 350) - Sleep, 1000 - adbClick(136, 350) - Delay(1) - } - if(FindOrLoseImage("Create_SignupsDataSharing", 0, failSafeTime)) { - adbClick(82, 287) - Sleep, 1000 - adbClick(80, 355) - Sleep, 100 - adbClick(90, 425) - Sleep, 100 - adbClick(141, 484) - Delay(1) - } - if(FindImageAndClick("Create_BeginNewAccountButton", 145, 484, , , 2, failSafeTime)) ;wait to be at create save data screen while clicking - break - Delay(1) - adbClick_wbb(261, 406) - if(FindImageAndClick("Create_BeginNewAccountButton", 145, 484, , , 2, failSafeTime)) ;wait to be at create save data screen while clicking - break - Delay(1) - adbClick_wbb(261, 374) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for Save`n(" . failSafeTime . "/45 seconds)") - } - - Delay(1) - - adbClick_wbb(143, 348) - - Delay(1) - - failSafeTime := 0 - match := "" - Loop { - if(FindOrLoseImage("Create_NintendoLink", 0, failSafeTime)) { - match :="Create_NintendoLink" - break - } - if(FindOrLoseImage("Create_NintendoLink2", 0, failSafeTime)) { - match :="Create_NintendoLink2" - break - } - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for Nintendo Link`n(" . failSafeTime . "/45 seconds)") - } - Delay(1) - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - if(FindOrLoseImage(match, 0, failSafeTime)){ - adbClick_wbb(140, 460) - Loop { - Delay(1) - if(FindOrLoseImage(match, 1, failSafeTime)){ - adbClick_wbb(140, 380) ; click ok on the interrupted while opening pack prompt - break - } - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - } - } else if(FindOrLoseImage("Create_DownloadAlertWindow", 0, failSafeTime)){ - adbClick_wbb(203, 364) - } else if(FindOrLoseImage("Create_DownloadComplete", 0, failSafeTime)){ - adbClick_wbb(140, 370) - } else if(FindOrLoseImage("Create_CinematicBackground", 0, failSafeTime)){ - break - } - Delay(1) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - } - - if(session.get("setSpeed") = 3){ - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - FindImageAndClick(GetSpeedModNeedle(1), GetSpeedModClickX(1), GetSpeedModClickY(1)) - Delay(1) - adbClick_wbb(51, 297) - Delay(1) - } - - FindImageAndClick("Create_WelcomePopup", 253, 506, , 110) ;click through cutscene until welcome page - - if(session.get("setSpeed") = 3){ - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) - Delay(1) - adbClick_wbb(51, 297) - Delay(1) - } - FindImageAndClick("Create_NameInputIcon", 189, 438) ;wait for name input screen - FindImageAndClick("Create_DeactivatedOKButton", 139, 257) ;wait for name input screen - - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - if (botConfig.get("AccountName") != "ERROR" && botConfig.get("AccountName") != "") { - Random, randomNum, 1, 500 ; Generate random number from 1 to 500 - username := botConfig.get("AccountName") . "-" . randomNum - username := SubStr(username, 1, 14) ; max character limit - LogDebug("Using AccountName: " . username) - } else { - fileName := A_ScriptDir . "\..\usernames.txt" - if(FileExist(fileName)) - name := ReadFile("usernames") - else - name := ReadFile("usernames_default") - - Random, randomIndex, 1, name.MaxIndex() - username := name[randomIndex] - username := SubStr(username, 1, 14) ; max character limit - LogDebug("Using random username: " . username) - } - - adbInput(username) - Delay(1) - if(FindImageAndClick("Create_PackReturnButtonIcon", 185, 372, , , 10)) - break - adbClick_wbb(90, 370) - Delay(1) - adbClick_wbb(139, 254) ; 139 254 194 372 - Delay(1) - adbClick_wbb(139, 254) - Delay(1) - EraseInput() ; incase the random pokemon is not accepted - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("In failsafe for Trace. " . failSafeTime . "/45 seconds") - if(failSafeTime > 45) - restartGameInstance("Stuck at name") - } - - Delay(1) - - adbClick_wbb(140, 424) - - FindImageAndClick("Pack_ReadyForOpenPack", 140, 424) ;wait for pack to be ready to trace - if(session.get("setSpeed") > 1) { - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - FindImageAndClick(GetSpeedModNeedle(1), GetSpeedModClickX(1), GetSpeedModClickY(1)) - Delay(1) - adbClick_wbb(51, 297) - Delay(1) - } - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - adbSwipe_wbb(adbSwipeParams) - Sleep, 100 - if(FindOrLoseImage("Pack_ReadyForOpenPack", 1, failSafeTime)){ - if(session.get("setSpeed") > 1) { - if(session.get("setSpeed") = 3) { - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) ; click 3x - } - } - adbClick_wbb(51, 297) - break - } - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for Pack`n(" . failSafeTime . "/45 seconds)") - } - - FindImageAndClick("Create_SwipeForRegisterDexIcon", 140, 375) ;click through cards until needing to swipe up - if(session.get("setSpeed") > 1) { - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - FindImageAndClick(GetSpeedModNeedle(1), GetSpeedModClickX(1), GetSpeedModClickY(1)) - Delay(1) - adbClick_wbb(51, 297) - Delay(1) - } - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - adbSwipe_wbb("266 770 266 355 60") - Sleep, 100 - if(FindOrLoseImage("Create_ConfirmRegisteredCard", 0, failSafeTime)){ - if(session.get("setSpeed") > 1) { - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - if(session.get("setSpeed") = 3) - FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) - else - FindImageAndClick(GetSpeedModNeedle(2), GetSpeedModClickX(2), GetSpeedModClickY(2)) - } - adbClick_wbb(51, 297) - break - } - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for swipe up for " . failSafeTime . "/45 seconds") - Delay(1) - } - - Delay(1) - adbClick_wbb(204, 371) - - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - adbClick_wbb(137, 365) - Delay(1) - adbClick_wbb(137, 480) - Delay(1) - if(FindOrLoseImage("Create_MustClickMissionBackground", 0, failSafeTime)){ - break - } else if(FindOrLoseImage("Create_DownloadAlertWindow", 0, failSafeTime)){ - adbClick_wbb(203, 364) - } - } - - Delay(1) - adbClick_wbb(247, 472) - - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - if(FindOrLoseImage("Create_TutorialPackOpenNotifyIcon", 0, failSafeTime)) { - break - } - adbClick_wbb(90, 260) - adbClick_wbb(140, 400) - adbClick_wbb(137, 340) - Delay(3) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for pack notification " . failSafeTime . "/45 seconds") - } - - FindImageAndClick("Create_TutorialPackOpenNotifyIcon", 145, 194) ;click on packs. stop at booster pack tutorial - - Delay(3) - adbClick_wbb(142, 436) - Delay(3) - adbClick_wbb(142, 436) - Delay(3) - adbClick_wbb(142, 436) - Delay(3) - adbClick_wbb(142, 436) - - FindImageAndClick("Pack_ReadyForOpenPack", 239, 497) ;wait for pack to be ready to Trace - if(session.get("setSpeed") > 1) { - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - FindImageAndClick(GetSpeedModNeedle(1), GetSpeedModClickX(1), GetSpeedModClickY(1)) - Delay(1) - adbClick_wbb(51, 297) - Delay(1) - } - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - adbSwipe_wbb(adbSwipeParams) - Sleep, 100 - if(FindOrLoseImage("Pack_ReadyForOpenPack", 1, failSafeTime)){ - if(session.get("setSpeed") > 1) { - if(session.get("setSpeed") = 3) { - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) - } - } - adbClick_wbb(51, 297) - break - } - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for Pack`n(" . failSafeTime . "/45 seconds)") - Delay(1) - } - - FindImageAndClick("Pack_ResultAfterOpenPack", 252, 505, 5, 50) ;skip through cards until results opening screen - - FindImageAndClick("Pack_SkipButtonAfterOpenPack", 146, 496) ;click on next until skip button appears - - FindImageAndClick("Pack_NextButtonAfterOpenPack", 239, 497, , , 2) - - FindImageAndClick("Create_UnlockedWonerPickIconInLevelUp", 146, 494) ;click on next until skip button appearsstop at hourglasses tutorial - - Delay(3) - - adbClick_wbb(140, 358) - - FindImageAndClick("Common_ShopButtonInMain", 146, 444) ;click until at main menu - - ; New needle & search region 11.1.2025 kevinnnn - FindImageAndClick("Create_CardImageInTutorialWPFirstScreen", 79, 411) - - FindImageAndClick("Create_WPItemBottomBorder", 190, 437) ; click through tutorial - - Delay(2) - - adbClick_wbb(202, 347) ; select Wonder Pick item to open Bonus Pick confirm - Delay(2) - - ; Bonus Pick confirm (No cost / OK). Wonder4 selection needle often never matches on current UI. - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - adbClick_wbb(208, 461) ; OK - Delay(1) - if(FindOrLoseImage("Create_TitleBottomBorderInWPSelectCard", 0, failSafeTime)) - break - ; Dialog may not have opened yet — retry select - adbClick_wbb(202, 347) - Delay(1) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Confirming WonderPick`n(" . failSafeTime . "/45 seconds)") - } - - if(session.get("setSpeed") = 3) ;time the animation - Sleep, 1500 - else - Sleep, 2500 - - Delay(1) - - adbClick_wbb(187, 345) - - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - if(session.get("setSpeed") = 3) - continueTime := 1 - else - continueTime := 3 - - if (TryWonderPickDexSwipeRegister()) { - failSafeTime := 0 - } else if(FindOrLoseImage("Pack_SkipButtonAfterOpenPack", 0, failSafeTime)) { - adbClick_wbb(239, 497) - } else if(FindOrLoseImage("Create_WelcomePopup", 0, failSafeTime)) { ;click through to end of tut screen - break - } else if(FindOrLoseImage("Pack_NextButtonAfterOpenPack", 0, failSafeTime)) { - adbClick_wbb(146, 494) ;146, 494 - } else if(FindOrLoseImage("Next2", 0, failSafeTime)) { - adbClick_wbb(146, 494) ;146, 494 - } else { - adbClick_wbb(187, 345) - Delay(1) - adbClick_wbb(143, 492) - Delay(1) - adbClick_wbb(143, 492) - Delay(1) - } - Delay(1) - - ; adbClick_wbb(66, 446) - ; Delay(1) - ; adbClick_wbb(66, 446) - ; Delay(1) - ; adbClick_wbb(66, 446) - ; Delay(1) - ; adbClick_wbb(187, 345) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for End`n(" . failSafeTime . "/45 seconds)") - } + ; Create the borders and show + Gui, BoundingBox%guiSuffix%:Add, Progress, x%Xm2% y%Ym2% w%BoxWidth% h2 %color% + Gui, BoundingBox%guiSuffix%:Add, Progress, x%Xm2% y%Ym2% w2 h%BoxHeight% %color% + Gui, BoundingBox%guiSuffix%:Add, Progress, x%Xh2% y%Ym2% w2 h%BoxHeight% %color% + Gui, BoundingBox%guiSuffix%:Add, Progress, x%Xm2% y%Yh2% w%BoxWidth% h2 %color% - FindImageAndClick("Create_FullFreepackInMainCenter", 192, 449) ;click until at main menu - EnterGameFromWelcomeIfNeeded() ; 1.7.0 forces restart after WP onto Welcome before GoToMain + xshow := xwin + yshow := ywin + Gui, BoundingBox%guiSuffix%:Show, x%xshow% y%yshow% NoActivate + Sleep, 100 - return true } -ensureMissionUserPrefsExist() { +adbSwipe_wbb(params) { + prof := Prof_Scope(A_ThisFunc) global session - if(!doesMissionUserPrefsExist()) { - session.set("failSafe", A_TickCount) - failSafeTime := 0 - ; Click for hamburger menu and wait for profile - Loop { - adbClick(240, 494) - if(FindOrLoseImage("Profile_UserNameArrowInSettingMenu", 0, failSafeTime)) { - break - } else { - clickButton := FindOrLoseImage("Common_ColorChangeButton", 0, , 80) - if(clickButton) { - StringSplit, pos, clickButton, `, ; Split at ", " - adbClick(pos1, pos2) - } - } - Delay(1) - failSafeTime := (A_TickCount - failSafe) // 1000 - } - - FindImageAndClick("Profile_EditNameButtonIcon", 210, 140, , 200) ; Open profile/stats page and wait - session.set("failSafe", A_TickCount) - failSafeTime := 0 - ; Click for hamburger menu and wait for profile - Loop { - adbInputEvent("111") ;send ESC - if(FindOrLoseImage("Profile_UserNameArrowInSettingMenu", 0, failSafeTime)) { - break - } else { - clickButton := FindOrLoseImage("Common_ColorChangeButton", 0, , 80) - if(clickButton) { - StringSplit, pos, clickButton, `, ; Split at ", " - adbClick(pos1, pos2) - } - } - Delay(1) - failSafeTime := (A_TickCount - failSafe) // 1000 - } - - } -} - -FindHourglassOpenConfirmation(tenPackOpening, failSafeTime) { - if (tenPackOpening) - return (FindOrLoseImage(67, 446, 83, 468, , "HourglassPack10", 0, failSafeTime) || FindOrLoseImage(45, 446, 60, 465, , "HourGlassAndPokeGoldPack10", 0, failSafeTime) || FindOrLoseImage("Pack_PokeGoldImageAfterOpenPackClick", 0, failSafeTime) || FindOrLoseImage(66, 447, 84, 465, , "PokeGoldPackNoHourglasses", 0, failSafeTime)) - - return (FindOrLoseImage("Pack_HourglassImageAfterOpenPackClick", 0, failSafeTime) || FindOrLoseImage("Pack_HourglassAndPokeGoldImageAfterOpenPackClick", 0, failSafeTime) || FindOrLoseImage("Pack_PokeGoldImageAfterOpenPackClick", 0, failSafeTime) || FindOrLoseImage(66, 447, 84, 465, , "PokeGoldPackNoHourglasses", 0, failSafeTime)) -} - -FindHourglassOpenConfirmationClosed(tenPackOpening, failSafeTime) { - if (tenPackOpening) - return (FindOrLoseImage(67, 446, 83, 468, , "HourglassPack10", 1, failSafeTime) && FindOrLoseImage(45, 446, 60, 465, , "HourGlassAndPokeGoldPack10", 1, failSafeTime) && FindOrLoseImage("Pack_PokeGoldImageAfterOpenPackClick", 1, failSafeTime) && FindOrLoseImage(66, 447, 84, 465, , "PokeGoldPackNoHourglasses", 1, failSafeTime)) - - return (FindOrLoseImage("Pack_HourglassImageAfterOpenPackClick", 1, failSafeTime) && FindOrLoseImage("Pack_HourglassAndPokeGoldImageAfterOpenPackClick", 1, failSafeTime) && FindOrLoseImage("Pack_PokeGoldImageAfterOpenPackClick", 1, failSafeTime) && FindOrLoseImage(66, 447, 84, 465, , "PokeGoldPackNoHourglasses", 1, failSafeTime)) -} - -DismissMainCloseAlertWindow(context := "") { - if(!FindOrLoseImage("Common_CloseAlertWindowInMain", 0, , , true)) - return false - LogInfo("Dismissed close-alert popup | context=" . context) - if(context != "") - CreateStatusMessage("Closing app quit confirmation`n" . context,,,, false) - else - CreateStatusMessage("Closing app quit confirmation",,,, false) - - adbClick_wbb(75, 365) - Delay(1) - return true + if(session.get("dbg_bbox")) + bboxAndPause_swipe(params, session.get("dbg_bboxNpause")) + adbSwipe(params) } -; After create-tutorial WP, 1.7.0 always forces an app restart onto Welcome (Tap to Start). -; ESC there opens quit confirmation — tap to enter instead. -; Returns true once past title screen (home/shop/news X/etc.). -EnterGameFromWelcomeIfNeeded(timeoutSec := 60) { +bboxAndPause_swipe(params, doPause := False) { global session - session.set("failSafe", A_TickCount) - Loop { - ; Already in-game (including News/X overlays) — let GoToMain use ESC. - if (FindOrLoseImage("Common_ShopButtonInMain", 0, 0, , true) - || FindOrLoseImage("Common_ActivatedHomeInMainMenu", 0, 0, , true) - || FindOrLoseImage("Pack_PackPointButton", 0, 0, , true) - || FindOrLoseImage("Create_FullFreepackInMainCenter", 0, 0, , true) - || FindOrLoseImage("WonderPick_WonderPickButtonInHome", 0, 0, , true) - || FindOrLoseImage("Common_PopupXButtonInMain", 0, 0, , true)) - return true - - if (DismissMainCloseAlertWindow("Welcome gate")) { - Delay(1) - adbClick_wbb(140, 450) ; Tap to Start under quit dialog - Delay(2) - CreateStatusMessage("Entering game from Welcome...") - continue - } - - if (FindOrLoseImage("Create_WelcomePopup", 0, 0, , true) - || FindOrLoseImage("Boot_Welcome", 0, 0, , true) - || FindOrLoseImage("Boot_Welcome2", 0, 0, , true)) { - adbClick_wbb(140, 450) ; Tap to Start - Delay(2) - CreateStatusMessage("Entering game from Welcome...") - continue - } - - Delay(1) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting past Welcome`n(" . failSafeTime . "/" . timeoutSec . " seconds)") - if (failSafeTime >= timeoutSec) - return false - } -} + guiSuffix := session.get("winTitle") + paramsplit := StrSplit(params , " ") + X1:=round(paramsplit[1] / 535 * 283) + Y1:=round((paramsplit[2] / 960 * 488) + 40) + X2:=round(paramsplit[3] / 535 * 283) + Y2:=round((paramsplit[4] / 960 * 488) + 40) + speed:=paramsplit[5] + CreateStatusMessage("Swiping (" . X1 . "," . Y1 . ") to (" . X2 . "," . Y2 . ") speed " . speed,,,, false) -WaitForPackPointButtonFromHome(clickX, clickY, context := "") { - global session + color := "BackgroundYellow" - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - if(DismissMainCloseAlertWindow(context)) { - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - continue - } + ;bboxDraw2(X1, Y1, X2, Y2, color) - adbClick_wbb(clickX, clickY) - Delay(0.5) - if(FindOrLoseImage("Pack_PackPointButton", 0, failSafeTime)) - return true + bboxDraw(X1-5, Y1-5, X1+5, Y1+5, color) + if (doPause) { + Pause + } + Gui, BoundingBox%guiSuffix%:Destroy - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for Points`n(" . failSafeTime . "/90 seconds)") + bboxDraw(X2-5, Y2-5, X2+5, Y2+5, color) + if (doPause) { + Pause } + Gui, BoundingBox%guiSuffix%:Destroy } -;------------------------------------------------------------------------------- -; EnterFavouritePackFromHome - switch to favourites view in Home, click the -; pack once to enter the Points screen, then wait for Pack_PackPointButton. -;------------------------------------------------------------------------------- -EnterFavouritePackFromHome() { +Gdip_ImageSearch_wbb(pBitmapHaystack,pNeedle,ByRef OutputList="" + ,OuterX1=0,OuterY1=0,OuterX2=0,OuterY2=0,Variation=0,Trans="" + ,SearchDirection=1,Instances=1,LineDelim="`n",CoordDelim=",") { + prof := Prof_Scope(A_ThisFunc) + profNeedle := Prof_Scope(A_ThisFunc . ":" . pNeedle.Name) global session - session.set("favEnteredFromHome", true) - - ; Skip favourites switch for packs already stable in Home. - mainScreenPacks := session.get("mainScreenPackList") - isStableInHome := false - homePosition := "" - if (IsObject(mainScreenPacks)) { - for pos, packName in mainScreenPacks { - if (packName = session.get("openPack")) { - isStableInHome := true - homePosition := pos - break - } - } - } + bias := MuMuBias() - if (!isStableInHome) { - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - adbClick_wbb(265, 236) - Delay(1) - if (FindOrLoseImage(245, 146, 251, 153, , "FavouriteBooster", 0, failSafeTime)) - break - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - if (failSafeTime >= 30) { - LogWarn("EnterFavouritePackFromHome: timed out waiting for FavouriteBooster", "ADB.txt") - break - } - } - } + vret := Gdip_ImageSearch(pBitmapHaystack,pNeedle.needle,OutputList,OuterX1,OuterY1+bias,OuterX2,OuterY2+bias,Variation,Trans,SearchDirection,Instances,LineDelim,CoordDelim) + if(session.get("dbg_bbox")) + bboxAndPause_immage(OuterX1, OuterY1+bias, OuterX2, OuterY2+bias, pNeedle, vret, session.get("dbg_bboxNpause")) + return vret +} + +GetNeedle(Path) { + prof := Prof_Scope(A_ThisFunc) + static NeedleBitmaps := Object() - ; For stable Home packs, use the Home position (Left/Middle/Right). - ; For favourite packs, use GetPackFavoriteHomeX (position within expansion). - mapHomeX := {"Left":60, "Middle":140, "Right":215} - if (isStableInHome) { - favHomeX := mapHomeX[homePosition] - if (favHomeX = "") - favHomeX := 140 - } else if (IsFunc("GetPackFavoriteHomeX")) { - favHomeX := GetPackFavoriteHomeX(session.get("openPack")) + if (NeedleBitmaps.HasKey(Path)) { + return NeedleBitmaps[Path] } else { - favHomeX := 140 + pNeedle := Gdip_CreateBitmapFromFile(Path) + needleObj := Object() + needleObj.Path := Path + pathsplit := StrSplit(Path , "\") + needleObj.Name := pathsplit[pathsplit.MaxIndex()] + needleObj.needle := pNeedle + NeedleBitmaps[Path] := needleObj + return needleObj } - - WaitForPackPointButtonFromHome(favHomeX, 203, "after fav pack select") } -RecoverPackOpeningToMainIfNeeded(caller := "") { - global session +DoTutorial() { + prof := Prof_Scope(A_ThisFunc) + global botConfig, session - foundRecoveryScreen := false + session.set("creationDate", A_Now) - if (FindOrLoseImage("Common_AlertForAppCrachDuringOpenPack", 0, 0, , true)) { - adbClick_wbb(139, 371) - Delay(2) - foundRecoveryScreen := true - } + FindImageAndClick("Create_CountryComboBoxButton", 143, 370) ;select month and year and click - if (!foundRecoveryScreen) { - foundRecoveryScreen := FindOrLoseImage("Create_NintendoLink", 0, 0, , true) - if (!foundRecoveryScreen) - foundRecoveryScreen := FindOrLoseImage("Create_DownloadAlertWindow", 0, 0, , true) - if (!foundRecoveryScreen) - foundRecoveryScreen := FindOrLoseImage("Create_DownloadComplete", 0, 0, , true) - if (!foundRecoveryScreen) - foundRecoveryScreen := FindOrLoseImage("Create_CinematicBackground", 0, 0, , true) - if (!foundRecoveryScreen) - foundRecoveryScreen := FindOrLoseImage("Create_WelcomePopup", 0, 0, , true) - if (!foundRecoveryScreen) - foundRecoveryScreen := FindOrLoseImage("StartupErrorX", 0, 0, , true) - if (!foundRecoveryScreen) - foundRecoveryScreen := FindOrLoseImage("Common_ShopButtonInMain", 0, 0, , true) - } + Delay(3) + adbClick_wbb(80, 400) + Delay(3) - if (!foundRecoveryScreen) - return false + tutorialLanguage := Trim(session.get("language")) + if (tutorialLanguage = "") { + ; New accounts have no metadata yet, so read the active game's language + if (EnsurePTCGPBHelperInstalled()) { + tutorialLanguage := adbWriteRaw("/data/ptcgp/ptcgpb lang", true) + tutorialLanguage := Trim(StrReplace(tutorialLanguage, "`r"), "`n`t ") + if (RegExMatch(tutorialLanguage, "i)^[a-z][a-z0-9_-]{0,15}$")) + session.set("language", tutorialLanguage) + else + tutorialLanguage := "" + } + } + StringLower, tutorialLanguage, tutorialLanguage + isYearInFront := (tutorialLanguage = "ja" || tutorialLanguage = "ko" || tutorialLanguage = "zh") + if (isYearInFront) { + ; JP/KR display the month picker in the opposite order. + Random, monthScrollCount, 4, 7 + Loop, %monthScrollCount% { + adbSwipe_wbb("100 707 100 903 150") + Delay(3) + } + } - LogInfo("Pack opening error recovery from " . caller . ": returning to selected pack screen", "Restart.txt") + adbClick_wbb(80, 375) + Delay(3) session.set("failSafe", A_TickCount) failSafeTime := 0 Loop { - if (FindOrLoseImage("Common_ShopButtonInMain", 0, 0, , true)) { - GoToMain() - return true - } - - if (FindOrLoseImage("Common_AlertForAppCrachDuringOpenPack", 0, 0, , true)) { - adbClick_wbb(139, 371) - } else if (FindOrLoseImage("StartupErrorX", 0, 0, , true)) { - adbClick_wbb(140, 439) - } else if (FindOrLoseImage("Create_DownloadAlertWindow", 0, 0, , true)) { - adbClick_wbb(203, 364) - } else if (FindOrLoseImage("Create_DownloadComplete", 0, 0, , true)) { - adbClick_wbb(140, 370) - } else if (FindOrLoseImage("Create_NintendoLink", 0, 0, , true)) { - adbClick_wbb(140, 460) - } else if (FindOrLoseImage("Create_WelcomePopup", 0, 0, , true)) { - adbClick_wbb(253, 506) - } else if (FindOrLoseImage("Create_CinematicBackground", 0, 0, , true)) { - adbClick_wbb(253, 506) - } else { - adbClick_wbb(140, 460) - } - - Delay(1) + Delay(3) + if(FindImageAndClick("Create_SelectedYear", , , , , 1, failSafeTime)) + break + Delay(3) + adbClick_wbb(142, 159) + Delay(3) + adbClick_wbb(80, 400) + Delay(3) + adbClick_wbb(80, 375) + Delay(3) + adbClick_wbb(82, 422) failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Recovering pack opening`n(" . failSafeTime . "/90 seconds)") - if (failSafeTime > 90) { - restartGameInstance("Stuck recovering pack opening error") - return false - } - } -} + CreateStatusMessage("Waiting for Year`n(" . failSafeTime . "/45 seconds)") + } ;select month and year and click -SelectPack(HG := false) { - global session + adbClick_wbb(200, 400) + Delay(3) - if(HG = "HGPack" || HG = "HGPack10") - session.set("packOpeningRecoveryPack", HG) - else - session.set("packOpeningRecoveryPack", "") - - ; define constants - mapPackX := {"Left":60, "Middle":140, "Right":215} - mainScreenPackCoords := {"MegaBlaziken":mapPackX["Left"], "Parade":mapPackX["Middle"], "CrimsonBlaze":mapPackX["Right"]} - - HomeScreenAllPackY := 203 - PackScreenAllPackY := 320 - - packx := getPackCoordXInHome() - packy := HomeScreenAllPackY - enteredPackScreenFromHome := false - - LogInfo("SelectPack: HG=" . HG . " packFavoriteSet=" . session.get("packFavoriteSet") . " isSkipSelectExpansion=" . session.get("isSkipSelectExpansion") . " openPack=" . session.get("openPack"), "ADB.txt") - - ; When favourite pack is set and we are coming from Home (not First boot), - ; switch to the favourites view in Home before clicking the pack. - ; Skip the switch for packs that are already stable in the Home screen - ; (listed in mainScreenPackList). - if (session.get("packFavoriteSet") && HG != "First") { - mainScreenPacks := session.get("mainScreenPackList") - isStableInHome := false - homePosition := "" - if (IsObject(mainScreenPacks)) { - for pos, packName in mainScreenPacks { - if (packName = session.get("openPack")) { - isStableInHome := true - homePosition := pos - break - } - } - } - if (!isStableInHome) { - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - adbClick_wbb(265, 236) - Delay(1) - if (FindOrLoseImage(245, 146, 251, 153, , "FavouriteBooster", 0, failSafeTime)) - break - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - if (failSafeTime >= 30) { - LogWarn("SelectPack: timed out waiting for FavouriteBooster in Home", "ADB.txt") - break - } - } - } - ; For stable Home packs, use the Home position (Left/Middle/Right). - ; For favourite packs, use GetPackFavoriteHomeX (position within expansion). - if (isStableInHome) { - packx := mapPackX[homePosition] - if (packx = "") - packx := 140 - } else if (IsFunc("GetPackFavoriteHomeX")) { - packx := GetPackFavoriteHomeX(session.get("openPack")) + if (!isYearInFront) { + ; Other languages display the year picker in the opposite order. + Random, yearScrollCount, 4, 7 + Loop, %yearScrollCount% { + adbSwipe_wbb("389 707 389 903 150") + Delay(3) } } + adbClick_wbb(200, 375) + Delay(3) + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { ;select month and year and click + Delay(3) + if(FindImageAndClick("Create_SelectedMonth", , , , , 1, failSafeTime)) + break + Delay(3) + adbClick_wbb(142, 159) + Delay(3) + adbClick_wbb(142, 159) + Delay(3) + adbClick_wbb(200, 400) + Delay(3) + adbClick_wbb(200, 375) + Delay(3) + adbClick_wbb(142, 159) + Delay(3) + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting for Month`n(" . failSafeTime . "/45 seconds)") + } ;select month and year and click - ensureMissionUserPrefsExist() - InitPackOpening() - if(HG = "First" && session.get("injectMethod") && session.get("loadedAccount") ){ - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - if(DismissMainCloseAlertWindow("Waiting for Points")) { - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - continue - } + Delay(3) + FindImageAndClick("Create_BirthConfirmCancelButton", 140, 474, , 1000) - ; When favourite pack is set, the game boots directly into the - ; Points screen. Don't click in Home, just wait for Points. - if (!session.get("packFavoriteSet")) - adbClick_wbb(packx, HomeScreenAllPackY) - Delay(1) - if(FindOrLoseImage("Pack_PackPointButton", 0, failSafeTime)) { - break - } - else if(!renew && !getFC) { - if(FindOrLoseImage("Common_AlertForAppCrachDuringOpenPack", 0)) { - adbClick_wbb(139, 371) - } - } - else if(FindOrLoseImage("Create_TutorialUseResourceForOpenPack", 0)) { - ;TODO hourglass tutorial still broken after injection - Delay(3) - adbClick_wbb(146, 441) - Delay(3) - adbClick_wbb(146, 441) - Delay(3) - adbClick_wbb(146, 441) - Delay(3) + ;wait date confirmation screen while clicking ok - FindImageAndClick("Create_TutorialPremiumPass", 168, 438, , 500, 5) ;stop at hourglasses tutorial 2 - Delay(1) + FindImageAndClick("Create_TosOpenButton", 203, 371, , 1000) ;wait to be at the tos screen while confirming birth - adbClick_wbb(203, 436) - FindImageAndClick("Create_InfoIconInStandByOpenPack", 180, 436, , 500) ;stop at hourglasses tutorial 2 180 to 203? - } + FindImageAndClick("Create_TosCloseButton", 139, 299, , 1000) ;wait for tos while clicking it - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for Points`n(" . failSafeTime . "/90 seconds)") - } - enteredPackScreenFromHome := true - } + FindImageAndClick("Create_TosOpenButton", 142, 486, , 1000) ;wait to be at the tos screen and click x - if (!enteredPackScreenFromHome) - FindImageAndClick("Pack_PackPointButton", packx, packy, , 1000) + FindImageAndClick("Common_PopupXButtonInMain", 142, 339, , 1000) ;wait to be at the tos screen - if(!session.get("isSkipSelectExpansion")) { - FindImageAndClick("Pack_ScrollInSelectExpansion", 248, 459, , 300) + FindImageAndClick("Create_TosOpenButton", 142, 486, , 1000) ;wait to be at the tos screen, click X - ; packs that can be opened after clicking A series - session.get("packCoordinates")[session.get("openPack")].moveSeriesScreen() - session.get("packCoordinates")[session.get("openPack")].expansionScreenDrag() + Delay(3) + adbClick_wbb(261, 374) - packx := session.get("packCoordinates")[session.get("openPack")].getXPos() - packy := session.get("packCoordinates")[session.get("openPack")].getYPos() + Delay(3) + adbClick_wbb(261, 406) - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop{ - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Move pack:" . session.get("openPack") . "`n(" . failSafeTime . "/90 seconds)") + Delay(3) + adbClick_wbb(145, 484) - adbClick_wbb(packx, packy) - Delay(2) - if(FindOrLoseImage("Pack_ScrollInSelectExpansion", 1, , , 1)) { - break - } + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + if(FindOrLoseImage("Create_SignupNotification", 0,failSafeTime)) { + ; notification flow + adbClick(138, 434) + Sleep, 1000 + adbClick(136, 350) + Sleep, 1000 + adbClick(136, 350) + Delay(1) } - Delay(2) - session.get("packCoordinates")[session.get("openPack")].additionalAction() - } - else if (session.get("packFavoriteSet")) { - ; Favourite pack: already on the correct expansion screen. - ; Nothing to do here. The pack selection click happens after - ; FindPackStats() below, so we don't interfere with it. - } - - if(HG = "First" && session.get("injectMethod") && session.get("loadedAccount") && !session.get("accountHasPackInfo")) { - FindPackStats() - } - - ; Favourite pack: now that FindPackStats is done (or skipped), click the - ; specific pack within the expansion to select it. Only needed on cold - ; boot (game boots with centre pack in foreground). When entering from - ; Home favourites, the clicked pack is already in foreground. - if (session.get("packFavoriteSet") && HG = "First" && !session.get("favEnteredFromHome")) { - if (IsFunc("GetPackFavoritePointsX")) - favPackX := GetPackFavoritePointsX(session.get("openPack")) - else - favPackX := 140 - if (favPackX != 140) { - adbClick_wbb(favPackX, PackScreenAllPackY) + if(FindOrLoseImage("Create_SignupsDataSharing", 0, failSafeTime)) { + adbClick(82, 287) + Sleep, 1000 + adbClick(80, 355) + Sleep, 100 + adbClick(90, 425) + Sleep, 100 + adbClick(141, 484) Delay(1) } + if(FindImageAndClick("Create_BeginNewAccountButton", 145, 484, , , 2, failSafeTime)) ;wait to be at create save data screen while clicking + break + Delay(1) + adbClick_wbb(261, 406) + if(FindImageAndClick("Create_BeginNewAccountButton", 145, 484, , , 2, failSafeTime)) ;wait to be at create save data screen while clicking + break + Delay(1) + adbClick_wbb(261, 374) + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting for Save`n(" . failSafeTime . "/45 seconds)") } - if(HG = "Tutorial") { - FindImageAndClick("Create_InfoIconInStandByOpenPack", 180, 436, , 500) ;stop at hourglasses tutorial 2 180 to 203? - } - else if(HG = "HGPack" || HG = "HGPack10") { - tenPackOpening := (HG = "HGPack10") - openPackCoord := session.get("packCoordinates")[session.get("openPack")] - if (IsObject(openPackCoord)) - openPackCoord.tapPackPreviewUntilPointsGone() - Delay(1) + Delay(1) + + adbClick_wbb(143, 348) - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - if (RecoverPackOpeningToMainIfNeeded("SelectPackConfirmation")) { - SelectPack(HG) - return - } - if(FindHourglassOpenConfirmation(tenPackOpening, failSafeTime)) { - break - }else if(FindOrLoseImage("Pack_NotEnoughItemsForOpenPack", 0)) { - session.set("cantOpenMorePacks", 1) - } - if(session.get("cantOpenMorePacks")) - return - openButtonX := tenPackOpening ? 70 : 161 - adbClick_wbb(openButtonX, 423) - Delay(1) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - statusText := tenPackOpening ? "Waiting for HourglassPack10" : "Waiting for HourglassPack3" - CreateStatusMessage(statusText . "`n(" . failSafeTime . "/45 seconds)") + Delay(1) + + failSafeTime := 0 + match := "" + Loop { + if(FindOrLoseImage("Create_NintendoLink", 0, failSafeTime)) { + match :="Create_NintendoLink" + break } - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - if (RecoverPackOpeningToMainIfNeeded("SelectPackConfirmationClosed")) { - SelectPack(HG) - return - } - if(FindHourglassOpenConfirmationClosed(tenPackOpening, failSafeTime)) { - break + if(FindOrLoseImage("Create_NintendoLink2", 0, failSafeTime)) { + match :="Create_NintendoLink2" + break + } + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting for Nintendo Link`n(" . failSafeTime . "/45 seconds)") + } + Delay(1) + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + if(FindOrLoseImage(match, 0, failSafeTime)){ + adbClick_wbb(140, 460) + Loop { + Delay(1) + if(FindOrLoseImage(match, 1, failSafeTime)){ + adbClick_wbb(140, 380) ; click ok on the interrupted while opening pack prompt + break + } + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 } - adbClick_wbb(205, 458) - Delay(1) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for HourglassPack4`n(" . failSafeTime . "/45 seconds)") + } else if(FindOrLoseImage("Create_DownloadAlertWindow", 0, failSafeTime)){ + adbClick_wbb(203, 364) + } else if(FindOrLoseImage("Create_DownloadComplete", 0, failSafeTime)){ + adbClick_wbb(140, 370) + } else if(FindOrLoseImage("Create_CinematicBackground", 0, failSafeTime)){ + break } - } else { - openPackCoord := session.get("packCoordinates")[session.get("openPack")] - if (IsObject(openPackCoord)) - openPackCoord.tapPackPreviewUntilPointsGone() Delay(1) + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + } - session.set("failSafe", A_TickCount) - failSafeTime := 0 - failsafeClickExecuted := false ; Flag to track if failsafe click has been executed - Loop { - adbClick_wbb(151, 420) ; open button - if(FindOrLoseImage("Pack_AnimationToReadyOpenPack", 0, failSafeTime)) { - break - } else if(FindOrLoseImage("Pack_NotEnoughItemsForOpenPack", 0)) { - session.set("cantOpenMorePacks", 1) - } else if(FindOrLoseImage("Pack_HourglassImageAfterOpenPackClick", 0, 1) || FindOrLoseImage("Pack_HourglassAndPokeGoldImageAfterOpenPackClick", 0, 1)) { - adbClick_wbb(205, 458) ; Handle unexpected HG pack confirmation - } else if(FindOrLoseImage("Common_AlertForAppCrachDuringOpenPack", 0)) { - ; Handle restart caused due to network error - adbClick_wbb(139, 371) - if (session.get("injectMethod") && session.get("loadedAccount") && session.get("friended")) { - IniWrite, 1, % session.get("scriptIniFile"), UserSettings, DeadCheck - } - LogInfo("[" . A_ScriptName . "] Stuck #1 in SelectPack.", "Restart.txt") - restartGameInstance("Stuck at pack opening") - return - } else { - adbClick_wbb(200, 451) ; Additional fallback click - } + if(session.get("setSpeed") = 3){ + FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) + FindImageAndClick(GetSpeedModNeedle(1), GetSpeedModClickX(1), GetSpeedModClickY(1)) + Delay(1) + adbClick_wbb(51, 297) + Delay(1) + } - if(session.get("cantOpenMorePacks")) - return + FindImageAndClick("Create_WelcomePopup", 253, 506, , 110) ;click through cutscene until welcome page - Delay(0.1) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for Skip2`n(" . failSafeTime . "/45 seconds)") - } + if(session.get("setSpeed") = 3){ + FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) + FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) + Delay(1) + adbClick_wbb(51, 297) + Delay(1) } -} + FindImageAndClick("Create_NameInputIcon", 189, 438) ;wait for name input screen + FindImageAndClick("Create_DeactivatedOKButton", 139, 257) ;wait for name input screen -PackOpening(tenPackOpening := false) { - global session - if (isTerminatePTCGPHelperApp()) { - InitPackOpening() - } - recoveryPack := tenPackOpening ? "HGPack10" : session.get("packOpeningRecoveryPack") session.set("failSafe", A_TickCount) failSafeTime := 0 - failsafeClickExecuted := false Loop { - if(recoveryPack != "" && RecoverPackOpeningToMainIfNeeded("PackOpening")) { - SelectPack(recoveryPack) - if(session.get("cantOpenMorePacks")) - return - session.set("failSafe", A_TickCount) - failSafeTime := 0 - failsafeClickExecuted := false - continue - } - ; 2nd+ pack: game may force Welcome Back missions tab while Waiting for Pack - if(TryRecoverWelcomeBackMissionsAfterPack("PackOpening", recoveryPack)) { - session.set("failSafe", A_TickCount) - failSafeTime := 0 - failsafeClickExecuted := false - continue - } - adbClick_wbb(146, 434) - Delay(0.2) - adbClick_wbb(170, 455) - if(FindOrLoseImage("Pack_ReadyForOpenPack", 0, failSafeTime)) { - break ;wait for pack to be ready to Trace and click skip - } else if(FindOrLoseImage("Pack_NotEnoughItemsForOpenPack", 0)) { - session.set("cantOpenMorePacks", 1) - } else if(FindOrLoseImage("Pack_HourglassImageAfterOpenPackClick", 0, 1) || FindOrLoseImage("Pack_HourglassAndPokeGoldImageAfterOpenPackClick", 0, 1)) { - adbClick_wbb(205, 453) ; handle unexpected no packs available - } else if(FindOrLoseImage("Pack_GetItemDialogAfterOpenPack", 0)){ - adbInputEvent("111") - Delay(2) + if (botConfig.get("AccountName") != "ERROR" && botConfig.get("AccountName") != "") { + Random, randomNum, 1, 500 ; Generate random number from 1 to 500 + username := botConfig.get("AccountName") . "-" . randomNum + username := SubStr(username, 1, 14) ; max character limit + LogDebug("Using AccountName: " . username) } else { - adbClick_wbb(239, 492) - } + fileName := A_ScriptDir . "\..\usernames.txt" + if(FileExist(fileName)) + name := ReadFile("usernames") + else + name := ReadFile("usernames_default") - ; Execute failsafe click only once after 10 seconds to try to select Floating Pack - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - if (failSafeTime >= 10 && !failsafeClickExecuted) { - if (FindOrLoseImage("Pack_PackPointButton", 0)) { - CreateStatusMessage("Trying to click floating pack...") - Sleep, 3000 - adbClick_wbb(151, 245) ; if pack is floating/glitched - failsafeClickExecuted := true - } + Random, randomIndex, 1, name.MaxIndex() + username := name[randomIndex] + username := SubStr(username, 1, 14) ; max character limit + LogDebug("Using random username: " . username) } - if(session.get("cantOpenMorePacks")) - return - + adbInput(username) + Delay(1) + if(FindImageAndClick("Create_PackReturnButtonIcon", 185, 372, , , 10)) + break + adbClick_wbb(90, 370) + Delay(1) + adbClick_wbb(139, 254) ; 139 254 194 372 + Delay(1) + adbClick_wbb(139, 254) + Delay(1) + EraseInput() ; incase the random pokemon is not accepted failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for Pack`n(" . failSafeTime . "/45 seconds)") - if(failSafeTime > 45){ - RemoveFriends() - if(session.get("injectMethod") && session.get("loadedAccount") && session.get("friended")) { - IniWrite, 1, % session.get("scriptIniFile"), UserSettings, DeadCheck - } - restartGameInstance("Stuck at Pack") - } + CreateStatusMessage("In failsafe for Trace. " . failSafeTime . "/45 seconds") + if(failSafeTime > 45) + restartGameInstance("Stuck at name") } + Delay(1) + + adbClick_wbb(140, 424) + + FindImageAndClick("Pack_ReadyForOpenPack", 140, 424) ;wait for pack to be ready to trace if(session.get("setSpeed") > 1) { FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) FindImageAndClick(GetSpeedModNeedle(1), GetSpeedModClickX(1), GetSpeedModClickY(1)) @@ -4280,203 +2406,95 @@ PackOpening(tenPackOpening := false) { Loop { adbSwipe_wbb(adbSwipeParams) Sleep, 100 - if (FindOrLoseImage("Pack_ReadyForOpenPack", 1, failSafeTime)){ + if(FindOrLoseImage("Pack_ReadyForOpenPack", 1, failSafeTime)){ if(session.get("setSpeed") > 1) { if(session.get("setSpeed") = 3) { FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) + FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) ; click 3x } } - adbClick_wbb(51, 292) + adbClick_wbb(51, 297) break } failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for Trace`n(" . failSafeTime . "/45 seconds)") - Delay(1) + CreateStatusMessage("Waiting for Pack`n(" . failSafeTime . "/45 seconds)") } - resultNeedle := tenPackOpening ? "Gift_ResultAfterOpenPack" : "Pack_ResultAfterOpenPack" - FindImageAndClick(resultNeedle, 252, 505, 5, 25) ;skip through cards until results opening screen - - CheckPack() - SetLastPackPulledNow() - ; A completed pack is forward progress even when the overall account task - ; runs longer than the monitor threshold. - writeLastActivityEpoch(session.get("scriptName")) - - if(!CardDetection_HasPendingGodPack() && !session.get("friendIDs") && botConfig.get("FriendID") = "" && session.get("accountOpenPacks") >= session.get("maxAccountPackNum")) - return - - ;FindImageAndClick("Pack_SkipButtonAfterOpenPack", 146, 494) ;click on next until skip button appears - + FindImageAndClick("Create_SwipeForRegisterDexIcon", 140, 375) ;click through cards until needing to swipe up + if(session.get("setSpeed") > 1) { + FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) + FindImageAndClick(GetSpeedModNeedle(1), GetSpeedModClickX(1), GetSpeedModClickY(1)) + Delay(1) + adbClick_wbb(51, 297) + Delay(1) + } session.set("failSafe", A_TickCount) failSafeTime := 0 Loop { - Delay(4) - if(FindOrLoseImage("Pack_SkipButtonAfterOpenPack", 0, failSafeTime)) { - adbClick_wbb(247, 500) - Delay(1) - } else if(FindOrLoseImage("Pack_NextButtonAfterOpenPack", 0, failSafeTime)) { - adbClick_wbb(146, 489) ;146, 494 - Delay(1) - } else if(FindOrLoseImage("Next2", 0, failSafeTime)) { - adbClick_wbb(146, 489) ;146, 494 - Delay(1) - } else if(FindOrLoseImage("Pack_BackButtonInSelectPackScreen", 0, failSafeTime)) { - break - } else if(FindOrLoseImage("Create_TutorialUseResourceForOpenPack", 0, failSafeTime)) { + adbSwipe_wbb("266 770 266 355 60") + Sleep, 100 + if(FindOrLoseImage("Create_ConfirmRegisteredCard", 0, failSafeTime)){ + if(session.get("setSpeed") > 1) { + FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) + if(session.get("setSpeed") = 3) + FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) + else + FindImageAndClick(GetSpeedModNeedle(2), GetSpeedModClickX(2), GetSpeedModClickY(2)) + } + adbClick_wbb(51, 297) break - } else { - adbClick_wbb(146, 489) ;146, 494 - Delay(1) } failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for Home`n(" . failSafeTime . "/45 seconds)") - if(failSafeTime > 45) - restartGameInstance("Stuck at Home") - } - - CardDetection_FlushPendingGodPack() -} - -HourglassOpening(HG := false, NEIRestart := true, tenPackOpening := false) { - global botConfig, session - if (isTerminatePTCGPHelperApp()) { - InitPackOpening() - } - recoveryPack := tenPackOpening ? "HGPack10" : "HGPack" - if(!HG) { - Delay(3) - adbClick_wbb(146, 441) ; 146 440 - Delay(3) - adbClick_wbb(146, 441) - Delay(3) - adbClick_wbb(146, 441) - Delay(3) - - FindImageAndClick("Create_TutorialPremiumPass", 168, 430, , 650, 5) ;stop at hourglasses tutorial 2 + CreateStatusMessage("Waiting for swipe up for " . failSafeTime . "/45 seconds") Delay(1) - - adbClick_wbb(203, 436) ; 203 436 - - if(session.get("packMethod")) { - PackMethod_RenewFriends() - SelectPack("Tutorial") - } - else { - FindImageAndClick("Create_InfoIconInStandByOpenPack", 180, 436, , 500) ;stop at hourglasses tutorial 2 180 to 203? - - if(session.get("cantOpenMorePacks")) - return - } } - if(!session.get("packMethod")) { - session.set("failSafe", A_TickCount) - failSafeTime := 0 - failsafeClickExecuted := false - recoveredPackOpening := false - Loop { - if (RecoverPackOpeningToMainIfNeeded("HourglassOpeningConfirmation")) { - SelectPack(recoveryPack) - if(session.get("cantOpenMorePacks")) - return - recoveredPackOpening := true - break - } - if(TryRecoverWelcomeBackMissionsAfterPack("HourglassOpening", recoveryPack)) { - session.set("failSafe", A_TickCount) - failSafeTime := 0 - failsafeClickExecuted := false - continue - } - if(FindHourglassOpenConfirmation(tenPackOpening, failSafeTime)) { - break - }else if(FindOrLoseImage("Pack_NotEnoughItemsForOpenPack", 0)) { - session.set("cantOpenMorePacks", 1) - } - if(session.get("cantOpenMorePacks")) - return - ; Execute failsafe click only once after 10 seconds to try to click floating pack - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - if (failSafeTime >= 10 && !failsafeClickExecuted) { - if (FindOrLoseImage("Pack_PackPointButton", 0)) { - CreateStatusMessage("Trying to click floating pack...") - Sleep, 3000 - adbClick_wbb(151, 250) ; if pack is floating/glitched - failsafeClickExecuted := true - } - } + Delay(1) + adbClick_wbb(204, 371) - if(failSafeTime >= 45) { - restartGameInstance("Stuck waiting for HourglassPack") - return - } - openButtonX := tenPackOpening ? 70 : 146 - adbClick_wbb(openButtonX, 434) - Delay(1) - statusText := tenPackOpening ? "Waiting for HourglassPack10" : "Waiting for HourglassPack" - CreateStatusMessage(statusText . "`n(" . failSafeTime . "/45 seconds)") - } - if(!recoveredPackOpening) { - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - if (RecoverPackOpeningToMainIfNeeded("HourglassOpeningConfirmationClosed")) { - SelectPack(recoveryPack) - if(session.get("cantOpenMorePacks")) - return - recoveredPackOpening := true - break - } - if(FindHourglassOpenConfirmationClosed(tenPackOpening, failSafeTime)) { - break - } - adbClick_wbb(205, 458) - Delay(1) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for HourglassPack2`n(" . failSafeTime . "/45 seconds)") - } - } - } session.set("failSafe", A_TickCount) failSafeTime := 0 Loop { - if(RecoverPackOpeningToMainIfNeeded("HourglassOpeningPackReady")) { - SelectPack(recoveryPack) - if(session.get("cantOpenMorePacks")) - return - session.set("failSafe", A_TickCount) - failSafeTime := 0 - continue - } - adbClick_wbb(146, 434) + adbClick_wbb(137, 365) + Delay(1) + adbClick_wbb(137, 480) Delay(1) - adbClick_wbb(170, 455) - if(FindOrLoseImage("Pack_ReadyForOpenPack", 0, failSafeTime)) - break ;wait for pack to be ready to Trace and click skip - else - adbClick_wbb(239, 497) + if(FindOrLoseImage("Create_MustClickMissionBackground", 0, failSafeTime)){ + break + } else if(FindOrLoseImage("Create_DownloadAlertWindow", 0, failSafeTime)){ + adbClick_wbb(203, 364) + } + } - if(session.get("cantOpenMorePacks")) - return + Delay(1) + adbClick_wbb(247, 472) - if(FindOrLoseImage("Common_ShopButtonInMain", 0, failSafeTime)){ - SelectPack(recoveryPack) - session.set("failSafe", A_TickCount) - failSafeTime := 0 + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + if(FindOrLoseImage("Create_TutorialPackOpenNotifyIcon", 0, failSafeTime)) { + break } - + adbClick_wbb(90, 260) + adbClick_wbb(140, 400) + adbClick_wbb(137, 340) + Delay(3) failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for Pack`n(" . failSafeTime . "/45 seconds)") - if(failSafeTime > 45) { - if(session.get("injectMethod") && session.get("loadedAccount") && session.get("friended")) { - IniWrite, 1, % session.get("scriptIniFile"), UserSettings, DeadCheck - } - restartGameInstance("Stuck at Pack") - } + CreateStatusMessage("Waiting for pack notification " . failSafeTime . "/45 seconds") } + FindImageAndClick("Create_TutorialPackOpenNotifyIcon", 145, 194) ;click on packs. stop at booster pack tutorial + + Delay(3) + adbClick_wbb(142, 436) + Delay(3) + adbClick_wbb(142, 436) + Delay(3) + adbClick_wbb(142, 436) + Delay(3) + adbClick_wbb(142, 436) + + FindImageAndClick("Pack_ReadyForOpenPack", 239, 497) ;wait for pack to be ready to Trace if(session.get("setSpeed") > 1) { FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) FindImageAndClick(GetSpeedModNeedle(1), GetSpeedModClickX(1), GetSpeedModClickY(1)) @@ -4489,7 +2507,7 @@ HourglassOpening(HG := false, NEIRestart := true, tenPackOpening := false) { Loop { adbSwipe_wbb(adbSwipeParams) Sleep, 100 - if (FindOrLoseImage("Pack_ReadyForOpenPack", 1, failSafeTime)){ + if(FindOrLoseImage("Pack_ReadyForOpenPack", 1, failSafeTime)){ if(session.get("setSpeed") > 1) { if(session.get("setSpeed") = 3) { FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) @@ -4500,278 +2518,158 @@ HourglassOpening(HG := false, NEIRestart := true, tenPackOpening := false) { break } failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for Trace`n(" . failSafeTime . "/45 seconds)") + CreateStatusMessage("Waiting for Pack`n(" . failSafeTime . "/45 seconds)") Delay(1) } - resultNeedle := tenPackOpening ? "Gift_ResultAfterOpenPack" : "Pack_ResultAfterOpenPack" - FindImageAndClick(resultNeedle, 252, 505, 5, 25) ;skip through cards until results opening screen + FindImageAndClick("Pack_ResultAfterOpenPack", 252, 505, 5, 50) ;skip through cards until results opening screen - CheckPack() - SetLastPackPulledNow() - ; A completed pack is forward progress even when the overall account task - ; runs longer than the monitor threshold. - writeLastActivityEpoch(session.get("scriptName")) + FindImageAndClick("Pack_SkipButtonAfterOpenPack", 146, 496) ;click on next until skip button appears - if(!CardDetection_HasPendingGodPack() && !session.get("friendIDs") && botConfig.get("FriendID") = "" && session.get("accountOpenPacks") >= session.get("maxAccountPackNum")) - return + FindImageAndClick("Pack_NextButtonAfterOpenPack", 239, 497, , , 2) - ;FindImageAndClick("Pack_SkipButtonAfterOpenPack", 146, 494) ;click on next until skip button appears + FindImageAndClick("Create_UnlockedWonerPickIconInLevelUp", 146, 494) ;click on next until skip button appearsstop at hourglasses tutorial - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - Delay(4) - if(FindOrLoseImage("Pack_SkipButtonAfterOpenPack", 0, failSafeTime)) { - adbClick_wbb(239, 497) - Delay(1) - } else if(FindOrLoseImage("Pack_NextButtonAfterOpenPack", 0, failSafeTime)) { - adbClick_wbb(146, 494) ;146, 494 - Delay(1) - } else if(FindOrLoseImage("Next2", 0, failSafeTime)) { - adbClick_wbb(146, 494) ;146, 494 - Delay(1) - } else if(FindOrLoseImage("Pack_BackButtonInSelectPackScreen", 0, failSafeTime)) { - break - } else { - adbClick_wbb(146, 494) ;146, 494 - Delay(1) - } - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for ConfirmPack`n(" . failSafeTime . "/45 seconds)") - if(failSafeTime > 45) - restartGameInstance("Stuck at ConfirmPack") - } + Delay(3) - CardDetection_FlushPendingGodPack() -} + adbClick_wbb(140, 358) -ReceiveGiftExtended() { - global session, receivedGiftOnly + FindImageAndClick("Common_ShopButtonInMain", 146, 444) ;click until at main menu - if (HasFlagInMetadata(session.get("accountFileName"), "R")) - return false + ; New needle & search region 11.1.2025 kevinnnn + FindImageAndClick("Create_CardImageInTutorialWPFirstScreen", 79, 411) - ; Reach gift screen with a timeout to avoid endless loops on UI desync. - foundClaimable := false - foundClaimedAll := false - minClaimableWaitSeconds := 5 - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - if(FindOrLoseImage("Common_ShopButtonInMain", 0)) { - adbClick_wbb(247, 93) - Delay(4) - } + FindImageAndClick("Create_WPItemBottomBorder", 190, 437) ; click through tutorial - if(FindOrLoseImage("Gift_Claimable", 0, 0, 25)) { - foundClaimable := true - break - } + Delay(2) - elapsedSeconds := (A_TickCount - session.get("failSafe")) // 1000 - if(elapsedSeconds >= minClaimableWaitSeconds && FindOrLoseImage("Gift_ClaimedAll", 0, 0, 25)) { - foundClaimedAll := true - break - } + adbClick_wbb(202, 347) ; select Wonder Pick item to open Bonus Pick confirm + Delay(2) - failSafeTime := elapsedSeconds - if(failSafeTime > 45) - break - } + ; Bonus Pick confirm (No cost / OK). Wonder4 selection needle often never matches on current UI. + session.set("failSafe", A_TickCount) failSafeTime := 0 - if(!foundClaimable) { - if(foundClaimedAll || (elapsedSeconds >= minClaimableWaitSeconds && FindOrLoseImage("Gift_ClaimedAll", 0, 0, 25))) - return false - return false - } - - maxClaimAttempts := 12 - claimConfirmed := false - Loop, %maxClaimAttempts% { - adbClick(212, 427) - Delay(3) - if (FindOrLoseImage("Gift_ReceivedWindowRightBorder", 0, 0, 25)) { - claimConfirmed := true - adbInputEvent("111") - Delay(2) - break - } - if(FindOrLoseImage("Pack_ReadyForOpenPack", 1, 0)){ - claimConfirmed := true - adbInputEvent("111") - Delay(2) + Loop { + adbClick_wbb(208, 461) ; OK + Delay(1) + if(FindOrLoseImage("Create_TitleBottomBorderInWPSelectCard", 0, failSafeTime)) break - } + ; Dialog may not have opened yet — retry select + adbClick_wbb(202, 347) + Delay(1) + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Confirming WonderPick`n(" . failSafeTime . "/45 seconds)") } - if(!claimConfirmed) - return false + if(session.get("setSpeed") = 3) ;time the animation + Sleep, 1500 + else + Sleep, 2500 Delay(1) - receivedGiftOnly := true - return true -} - -HandleGiftedPacksAfterReceiveGift() { - global session - - handledGiftPacks := 0 - maxGiftPacks := 20 - sawReceivedWindow := false - rewardSetHandled := false + adbClick_wbb(187, 345) session.set("failSafe", A_TickCount) failSafeTime := 0 - Loop { - if (rewardSetHandled || !FindOrLoseImage("Gift_Claimable", 0, 1)) { - if(FindOrLoseImage("Gift_ClaimedAll", 0, 1)) { - adbInputEvent("111") - Delay(1) - break - } - } - - if(FindOrLoseImage("Gift_ReceivedWindowRightBorder", 0, 1)) { - adbInputEvent("111") - Delay(1) - sawReceivedWindow := true - rewardSetHandled := true - session.set("failSafe", A_TickCount) - failSafeTime := 0 - continue - } - - if(FindOrLoseImage("Pack_ReadyForOpenPack", 0, 1)) { - HandleSingleGiftPackOpening() - handledGiftPacks++ - rewardSetHandled := true - - if(handledGiftPacks >= maxGiftPacks) - break + if(session.get("setSpeed") = 3) + continueTime := 1 + else + continueTime := 3 - session.set("failSafe", A_TickCount) + if (TryWonderPickDexSwipeRegister()) { failSafeTime := 0 - continue - } - - if(FindOrLoseImage("Pack_SkipButtonAfterOpenPack", 0, 1)) { - adbClick_wbb(247, 500) - } else if(FindOrLoseImage("Pack_NextButtonAfterOpenPack", 0, 1) || FindOrLoseImage("Next2", 0, 1)) { - adbClick_wbb(146, 489) - } else if(FindOrLoseImage("Pack_ResultAfterOpenPack", 0, 1)) { - adbClick_wbb(247, 500) + } else if(FindOrLoseImage("Pack_SkipButtonAfterOpenPack", 0, failSafeTime)) { + adbClick_wbb(239, 497) + } else if(FindOrLoseImage("Create_WelcomePopup", 0, failSafeTime)) { ;click through to end of tut screen + break + } else if(FindOrLoseImage("Pack_NextButtonAfterOpenPack", 0, failSafeTime)) { + adbClick_wbb(146, 494) ;146, 494 + } else if(FindOrLoseImage("Next2", 0, failSafeTime)) { + adbClick_wbb(146, 494) ;146, 494 } else { - adbClick_wbb(146, 489) + adbClick_wbb(187, 345) + Delay(1) + adbClick_wbb(143, 492) + Delay(1) + adbClick_wbb(143, 492) + Delay(1) } - Delay(1) + ; adbClick_wbb(66, 446) + ; Delay(1) + ; adbClick_wbb(66, 446) + ; Delay(1) + ; adbClick_wbb(66, 446) + ; Delay(1) + ; adbClick_wbb(187, 345) failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage((sawReceivedWindow ? "Finalizing gifts" : "Handling gifted packs") . "`n(" . failSafeTime . "/90 seconds)") - if(failSafeTime > 90) - break + CreateStatusMessage("Waiting for End`n(" . failSafeTime . "/45 seconds)") } -} - -SetOpenGiftSpeed(targetSpeed) { - if(targetSpeed != 1 && targetSpeed != 2 && targetSpeed != 3) - return false - SetOpenGiftSpeedByButtons(targetSpeed) + FindImageAndClick("Create_FullFreepackInMainCenter", 192, 449) ;click until at main menu + EnterGameFromWelcomeIfNeeded() ; 1.7.0 forces restart after WP onto Welcome before GoToMain return true } -SetOpenGiftSpeedByButtons(targetSpeed) { - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - if(targetSpeed = 1) - FindImageAndClick(GetSpeedModNeedle(1), GetSpeedModClickX(1), GetSpeedModClickY(1)) - else if(targetSpeed = 3) - FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) +DismissMainCloseAlertWindow(context := "") { + if(!FindOrLoseImage("Common_CloseAlertWindowInMain", 0, , , true)) + return false + + LogInfo("Dismissed close-alert popup | context=" . context) + if(context != "") + CreateStatusMessage("Closing app quit confirmation`n" . context,,,, false) else - FindImageAndClick(GetSpeedModNeedle(2), GetSpeedModClickX(2), GetSpeedModClickY(2)) + CreateStatusMessage("Closing app quit confirmation",,,, false) - Delay(1) - adbClick_wbb(51, 297) + adbClick_wbb(75, 365) Delay(1) return true } -HandleSingleGiftPackOpening() { - global session, adbSwipeParams - - session.set("openedGiftPack", true) - - if(session.get("setSpeed") > 1) { - SetOpenGiftSpeed(1) - } +; After create-tutorial WP, 1.7.0 always forces an app restart onto Welcome (Tap to Start). +; ESC there opens quit confirmation — tap to enter instead. +; Returns true once past title screen (home/shop/news X/etc.). +EnterGameFromWelcomeIfNeeded(timeoutSec := 60) { + global session session.set("failSafe", A_TickCount) - failSafeTime := 0 Loop { - adbSwipe_wbb(adbSwipeParams) - Sleep, 100 - ; Confirm swipe by requiring both ready markers to stay gone for a short window. - if(FindOrLoseImage("Pack_ReadyForOpenPack", 1, failSafeTime)) { - swipeConfirmed := true - Loop, 4 { - Sleep, 150 - if(FindOrLoseImage("Pack_ReadyForOpenPack", 0, 0, 20, 1)) { - swipeConfirmed := false - break - } - } - - if(swipeConfirmed) { - if(session.get("setSpeed") > 1) { - SetOpenGiftSpeed(session.get("setSpeed")) - } - break - } - } + ; Already in-game (including News/X overlays) — let GoToMain use ESC. + if (FindOrLoseImage("Common_ShopButtonInMain", 0, 0, , true) + || FindOrLoseImage("Common_ActivatedHomeInMainMenu", 0, 0, , true) + || FindOrLoseImage("Pack_PackPointButton", 0, 0, , true) + || FindOrLoseImage("Create_FullFreepackInMainCenter", 0, 0, , true) + || FindOrLoseImage("WonderPick_WonderPickButtonInHome", 0, 0, , true) + || FindOrLoseImage("Common_PopupXButtonInMain", 0, 0, , true)) + return true - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Opening gifted pack`n(" . failSafeTime . "/45 seconds)") - if(failSafeTime > 45) { - if(session.get("setSpeed") > 1) { - SetOpenGiftSpeed(session.get("setSpeed")) - } - restartGameInstance("Stuck at gifted pack swipe") - return + if (DismissMainCloseAlertWindow("Welcome gate")) { + Delay(1) + adbClick_wbb(140, 450) ; Tap to Start under quit dialog + Delay(2) + CreateStatusMessage("Entering game from Welcome...") + continue } - } - FindImageAndClick("Gift_ResultAfterOpenPack", 252, 505, 5, 25) - - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - if(FindOrLoseImage("Gift_ReceivedWindowRightBorder", 0, 1)) { - break - } else if(FindOrLoseImage("Pack_ReadyForOpenPack", 0, 1)) { - break - } else if(FindOrLoseImage("Create_SwipeForRegisterDexIcon", 0, 1)) { - break - } else if(FindOrLoseImage("Pack_SkipButtonAfterOpenPack", 0, 1)) { - adbClick_wbb(247, 500) - } else if(FindOrLoseImage("Pack_NextButtonAfterOpenPack", 0, 1) || FindOrLoseImage("Next2", 0, 1)) { - adbClick_wbb(146, 489) - } else { - adbClick_wbb(146, 489) + if (FindOrLoseImage("Create_WelcomePopup", 0, 0, , true) + || FindOrLoseImage("Boot_Welcome", 0, 0, , true) + || FindOrLoseImage("Boot_Welcome2", 0, 0, , true)) { + adbClick_wbb(140, 450) ; Tap to Start + Delay(2) + CreateStatusMessage("Entering game from Welcome...") + continue } Delay(1) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Advancing gifted pack`n(" . failSafeTime . "/45 seconds)") - if(failSafeTime > 45) - break + CreateStatusMessage("Waiting past Welcome`n(" . failSafeTime . "/" . timeoutSec . " seconds)") + if (failSafeTime >= timeoutSec) + return false } - - writeLastActivityEpoch(session.get("scriptName")) } ; Wonder Pick reveals can show the first-time card-dex register tutorial. @@ -5489,13 +3387,3 @@ GoToMain() { CreateStatusMessage("Moving to Main`n(" . failSafeTime . "/45 seconds)") } } - -CleanupBeforeExit(){ - global session - - TerminateHelper() - CloseCardDatabase(session.get("deviceAccount")) - AccountMetadata_CloseTempForInstance(session.get("scriptName")) - allSpecialEventDispose() - GetGPUMemoryByPDH(-1, true) -} diff --git a/Scripts/Include/BotPackEvaluation.ahk b/Scripts/Include/BotPackEvaluation.ahk new file mode 100644 index 00000000..b3409708 --- /dev/null +++ b/Scripts/Include/BotPackEvaluation.ahk @@ -0,0 +1,877 @@ +; Pack result reading, card evaluation and god pack handling + +InitPackOpening(full := false) { + prof := Prof_Scope(A_ThisFunc) + + if (!EnsurePTCGPBHelperInstalled()) + return false + adbWriteRaw("rm -f /data/ptcgp/result.rc") + adbWriteRaw("rm -f /data/ptcgp/result.log") + adbWriteRaw("pkill -f /data/ptcgp/ptcgpb") + + SavePackOpeningMissionUserPrefsSnapshot("pre") + StartPtcgpbWatchCards(full) +} + +PackOpeningMissionUserPrefsPath() { + return "/data/data/jp.pokemon.pokemontcgp/files/UserPreferences/v1/MissionUserPrefs" +} + +PackOpeningMissionUserPrefsSnapshotPath(kind) { + global session + return "/data/ptcgp/" . session.get("scriptName") . "_" . kind +} + +SavePackOpeningMissionUserPrefsSnapshot(kind) { + sourcePath := PackOpeningMissionUserPrefsPath() + snapshotPath := PackOpeningMissionUserPrefsSnapshotPath(kind) + + adbWriteRaw("mkdir -p /data/ptcgp") + adbWriteRaw("rm -f " . snapshotPath) + adbWriteRaw("if [ -f " . sourcePath . " ]; then cp -f " . sourcePath . " " . snapshotPath . "; fi") +} + +PullPackOpeningMissionUserPrefsSnapshot(kind, failedDir, uniquePrefix) { + global session + + if !FileExist(failedDir) + FileCreateDir, %failedDir% + + remotePath := PackOpeningMissionUserPrefsSnapshotPath(kind) + sdcardPath := "/sdcard/" . uniquePrefix . "_" . kind . "_MissionUserPrefs" + localPath := failedDir . "\" . uniquePrefix . "_" . kind + + if (FileExist(localPath)) + FileDelete, %localPath% + + adbWriteRaw("rm -f " . sdcardPath) + adbWriteRaw("if [ -f " . remotePath . " ]; then cp -f " . remotePath . " " . sdcardPath . " && chmod 666 " . sdcardPath . "; fi") + RunWait, % """" . session.get("adbPath") . """ -s 127.0.0.1:" . session.get("adbPort") . " pull """ . sdcardPath . """ """ . localPath . """",, Hide + adbWriteRaw("rm -f " . sdcardPath) + + exists := FileExist(localPath) ? true : false + return { kind: kind, remotePath: remotePath, localPath: localPath, exists: exists } +} + +PullPackOpeningResultLog(failedDir, uniquePrefix) { + global session + + debugLogFile := "debug_cards.txt" + if !FileExist(failedDir) + FileCreateDir, %failedDir% + + remotePath := "/data/ptcgp/result.log" + sdcardPath := "/sdcard/" . uniquePrefix . "_result.log" + localPath := failedDir . "\" . uniquePrefix . "_result.log" + + remoteStatus := Trim(StrReplace(adbWriteRaw("if [ -f " . remotePath . " ]; then echo present:$(wc -c < " . remotePath . "); else echo missing; fi", true), "`r"), "`n`t ") + LogWarn("PullPackOpeningResultLog begin | remote=" . remotePath . " | remoteStatus=" . remoteStatus . " | staging=" . sdcardPath . " | local=" . localPath, debugLogFile) + + localDeleteStatus := "not-needed" + if (FileExist(localPath)) { + FileDelete, %localPath% + localDeleteStatus := ErrorLevel ? "failed" : "ok" + } + + stagingCleanupStatus := Trim(StrReplace(adbWriteRaw("if rm -f " . sdcardPath . "; then echo ok; else echo failed; fi", true), "`r"), "`n`t ") + stagingCopyStatus := Trim(StrReplace(adbWriteRaw("if [ ! -f " . remotePath . " ]; then echo remote-missing; elif cp -f " . remotePath . " " . sdcardPath . " && chmod 666 " . sdcardPath . "; then echo ok; else echo failed; fi", true), "`r"), "`n`t ") + RunWait, % """" . session.get("adbPath") . """ -s 127.0.0.1:" . session.get("adbPort") . " pull """ . sdcardPath . """ """ . localPath . """",, Hide + pullExitCode := ErrorLevel + finalCleanupStatus := Trim(StrReplace(adbWriteRaw("if rm -f " . sdcardPath . "; then echo ok; else echo failed; fi", true), "`r"), "`n`t ") + + exists := FileExist(localPath) ? true : false + localSize := 0 + resultLogContent := "(local result.log is missing)" + readSucceeded := false + if (exists) { + FileGetSize, localSize, %localPath% + FileRead, resultLogContent, %localPath% + readError := ErrorLevel + readSucceeded := !readError + if (!readSucceeded) + resultLogContent := "(failed to read local result.log; ErrorLevel=" . readError . ")" + else if (resultLogContent = "") + resultLogContent := "(empty)" + } + + LogWarn("PullPackOpeningResultLog transfer | localDelete=" . localDeleteStatus . " | initialCleanup=" . stagingCleanupStatus . " | stagingCopy=" . stagingCopyStatus . " | adbPullExitCode=" . pullExitCode . " | finalCleanup=" . finalCleanupStatus . " | localExists=" . (exists ? "yes" : "no") . " | localBytes=" . localSize . " | read=" . (readSucceeded ? "ok" : "unavailable"), debugLogFile) + LogWarn("PullPackOpeningResultLog contents begin`n" . resultLogContent . "`nPullPackOpeningResultLog contents end", debugLogFile) + + return { kind: "result.log", remotePath: remotePath, localPath: localPath, exists: exists, remoteStatus: remoteStatus, pullExitCode: pullExitCode, localSize: localSize, readSucceeded: readSucceeded } +} + +ReportPackRecognitionFailure(reason := "Card Recognition Failed, use fallback mechanism") { + global session, botConfig + + root := getScriptBaseFolder() + failedDir := root . "\Logs\failed" + uniquePrefix := A_Now . "_" . session.get("scriptName") . "_pack" . session.get("packsInPool") + + preSnapshot := PullPackOpeningMissionUserPrefsSnapshot("pre", failedDir, uniquePrefix) + postSnapshot := PullPackOpeningMissionUserPrefsSnapshot("post", failedDir, uniquePrefix) + resultLog := PullPackOpeningResultLog(failedDir, uniquePrefix) + + message := reason . "\nPlease submit these files for the bug report as well." + for _, snapshot in [preSnapshot, postSnapshot] { + localPathForMessage := StrReplace(snapshot.localPath, "\", "/") + if (snapshot.exists) { + message .= "\n" . snapshot.kind . ": " . localPathForMessage + LogWarn("Card recognition failure " . snapshot.kind . " MissionUserPrefs saved: " . snapshot.localPath, "debug_cards.txt") + } else { + message .= "\n" . snapshot.kind . ": missing (remote " . snapshot.remotePath . " was not available)" + LogWarn("Card recognition failure " . snapshot.kind . " MissionUserPrefs missing: " . snapshot.remotePath, "debug_cards.txt") + } + } + + resultLogPathForMessage := StrReplace(resultLog.localPath, "\", "/") + if (resultLog.exists) { + message .= "\n" . resultLog.kind . ": " . resultLogPathForMessage + LogWarn("Card recognition failure result.log saved: " . resultLog.localPath, "debug_cards.txt") + } else { + message .= "\n" . resultLog.kind . ": missing (remote " . resultLog.remotePath . " was not available)" + LogWarn("Card recognition failure result.log missing: " . resultLog.remotePath, "debug_cards.txt") + } + + ownerWebhookURL := botConfig.get("heartBeatOwnerWebHookURL") + if (ownerWebhookURL != "") + LogToDiscord(message,, false,,, ownerWebhookURL) + else + LogWarn("Owner Discord webhook URL is not configured. Card recognition failure message was not sent.", "Discord.txt") +} + +GetStdout(cmd) { + prof := Prof_Scope(A_ThisFunc) + shell := ComObjCreate("WScript.Shell") + exec := shell.Exec(cmd) + return exec.StdOut.ReadAll() +} + +TerminateHelper() { + adbWriteRaw("pkill -f /data/ptcgp/ptcgpb") +} + +ParsePackResultOutput(output) { + output := StrReplace(output, "`r") + output := Trim(output, "`n ") + lines := StrSplit(output, "`n") + parsedLines := [] + for _, line in lines { + line := Trim(line) + if (line != "") + parsedLines.Push(line) + } + lines := parsedLines + + if (lines.Length() < 4) + return false + + pulls := [] + cards := [] + rarity := [] + packNames := [] + shinedust := 0 + raw_msg := "" + + idx := 1 + while (idx + 2 <= lines.Length()) { + pullCards := [] + for _, val in StrSplit(lines[idx], ",") { + card := Trim(val) + if (card != "") + pullCards.Push(card) + } + + pullPack := Trim(lines[idx + 1]) + + pullRarity := [] + for _, val in StrSplit(lines[idx + 2], ",") { + pullRarity.Push(Trim(val) + 0) + } + + if (pullCards.Length() = 0 || pullRarity.Length() = 0) + break + + pullShinedust := lines[idx + 3] + 0 + shinedust += pullShinedust + + pullRaw := lines[idx] . "`n" . lines[idx + 1] . "`n" . lines[idx + 2] . "`n" . lines[idx + 3] + pulls.Push({ cards: pullCards, pack: pullPack, rarity: pullRarity, shinedust: pullShinedust, raw: pullRaw }) + packNames.Push(pullPack) + + for _, card in pullCards + cards.Push(card) + for _, rare in pullRarity + rarity.Push(rare) + + if (raw_msg != "") + raw_msg .= "`n" + raw_msg .= pullRaw + + idx += 4 + } + + if (pulls.Length() = 0) + return false + + pack := "" + for _, packName in packNames { + if (pack != "") + pack .= ", " + pack .= packName + } + + return { cards: cards, pack: pack, rarity: rarity, shinedust: shinedust, raw: raw_msg, pulls: pulls } +} + +; Wait for the Helper to produce a parseable result.rc containing the cards, +; then read and parse it. Does not terminate the watcher (caller must do that +; once the result has been consumed). +ReadEliteDeckResult(timeoutSec := 45) { + prof := Prof_Scope(A_ThisFunc) + global session + + adbCommand := session.get("adbPath") . " -s 127.0.0.1:" . session.get("adbPort") + session.set("failSafe", A_TickCount) + Loop { + output := GetStdout(adbCommand . " shell cat /data/ptcgp/result.rc") + result := ParsePackResultOutput(output) + if (result) + return result + + Sleep, 500 + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting for Elite Deck result...`n(" . failSafeTime . "/" . timeoutSec . " seconds)") + if (failSafeTime >= timeoutSec) + break + } + return false +} + +UpdatePackCountAfterOpening(defaultOpenedPacks := 1) { + global session + + expectedOpenedPacks := session.get("expectedPackOpenCount") + 0 + if (expectedOpenedPacks >= 1 && expectedOpenedPacks <= 10) + defaultOpenedPacks := expectedOpenedPacks + session.set("expectedPackOpenCount", 1) + + oldPackCount := session.get("accountOpenPacks") + 0 + openedPackCount := defaultOpenedPacks + newPackCount := EvaluatePackCount() + + if (newPackCount = 0) { + session.set("accountOpenPacks", oldPackCount + defaultOpenedPacks) + } else { + packDelta := newPackCount - oldPackCount + if (packDelta >= 1 && packDelta <= 10) + openedPackCount := packDelta + + session.set("accountOpenPacks", newPackCount) + + if (session.get("injectMethod") && session.get("loadedAccount") && session.get("accountFileName") != "") { + accountMeta := AccountMetadata_Get(session.get("scriptName"), session.get("accountFileName"), session.get("loadedAccount")) + accountMeta["deviceAccount"] := GetCurrentDeviceAccountForMetadata() + accountMeta["packCount"] := newPackCount + AccountMetadata_SaveAccount(session.get("scriptName"), session.get("accountFileName"), accountMeta) + } + } + + if (session.get("injectMethod") && session.get("loadedAccount")) + UpdateAccount() + + session.set("packsInPool", session.get("packsInPool") + openedPackCount) + session.set("packsThisRun", session.get("packsThisRun") + openedPackCount) + + return openedPackCount +} + +EvaluatePack() { + prof := Prof_Scope(A_ThisFunc) + global session + adbCommand := session.get("adbPath") . " -s 127.0.0.1:" . session.get("adbPort") + expectedOpenedPacks := session.get("expectedPackOpenCount") + 0 + maxAttempts := expectedOpenedPacks > 1 ? 100 : 10 + foundResult := false + Loop, %maxAttempts% { + RunWait, % adbCommand . " shell test -f /data/ptcgp/result.rc", , Hide + if (ErrorLevel = 0) { + foundResult := true + break + } + Sleep, 300 + } + if (foundResult && expectedOpenedPacks > 1) + Sleep, 1000 + adbWriteRaw("pkill -f /data/ptcgp/ptcgpb") + waitadb() + + SavePackOpeningMissionUserPrefsSnapshot("post") + + output := GetStdout(adbCommand . " shell cat /data/ptcgp/result.rc") + parsedResult := ParsePackResultOutput(output) + if (!parsedResult && expectedOpenedPacks > 1) + LogDebug("EvaluatePack 10-pack parse failed. result.rc length=" . StrLen(output), "debug_cards.txt") + return parsedResult +} + +EvaluatePackCount() { + prof := Prof_Scope(A_ThisFunc) + + LogTrace("EvaluatePackCount running ptcgpb packcount", "ADB.txt") + return GetPtcgpbPackCount() +} + +RecoverPack() { + prof := Prof_Scope(A_ThisFunc) + global session + adbCommand := session.get("adbPath") . " -s 127.0.0.1:" . session.get("adbPort") + + pre := PackOpeningMissionUserPrefsSnapshotPath("pre") + post := PackOpeningMissionUserPrefsSnapshotPath("post") + + adbWriteRaw("rm -f /data/ptcgp/result.rc") + adbWriteRaw("rm -f /data/ptcgp/result.log") + adbWriteRaw("/data/ptcgp/ptcgpb diff-files --duplicate " . pre . " " . post) + output := GetStdout(adbCommand . " shell cat /data/ptcgp/result.rc") + return ParsePackResultOutput(output) +} + +CardDetection_QueueGodPack(validity, cards := "", finalizeAccount := false) { + global session + + session.set("keepAccount", true) + session.set("foundGP", true) + session.set("pendingGodPack", true) + session.set("pendingGodPackValidity", validity) + session.set("pendingGodPackCards", cards) + session.set("pendingGodPackFinalize", finalizeAccount) + session.set("manualVipValidity", validity) + session.set("pendingGodPackScreenshot", Screenshot(validity)) + LogInfo("Queued " . validity . " God Pack notification until pack cleanup finishes", "GPlog.txt") + return true +} + +CardDetection_HasPendingGodPack() { + global session + return session.get("pendingGodPack") +} + +CardDetection_CheckGodPackDeferred(invalidPack := false, cards := "", finalizeAccount := false) { + global botConfig, session + + currentPackInfo := session.get("currentPackInfo") + + normalBorders := currentPackInfo["TypeCount"]["normal"] + if (normalBorders) { + logMessage := "Instance: " . session.get("scriptName") " | Not a GP" + LogDebug(logMessage, "debug_cards.txt") + CreateStatusMessage("Not a God Pack...",,,, false) + return false + } + + session.set("keepAccount", true) + + if (!invalidPack) { + tempStarCount := currentPackInfo["TypeCount"]["fullart"] + currentPackInfo["TypeCount"]["rainbow"] + currentPackInfo["TypeCount"]["trainer"] + + logMessage := "Instance: " . session.get("scriptName") " | tempStarCount " . tempStarCount + LogDebug(logMessage, "debug_cards.txt") + + requiredStars := botConfig.get("minStars") + if (requiredStars > 0 && tempStarCount < requiredStars) { + CreateStatusMessage("Pack doesn't contain enough 2 stars...",,,, false) + invalidPack := true + } + } + + return CardDetection_QueueGodPack(invalidPack ? "Invalid" : "Valid", cards, finalizeAccount) +} + +CardDetection_FlushPendingGodPack() { + global session + + if (!CardDetection_HasPendingGodPack()) + return false + + validity := session.get("pendingGodPackValidity") + cards := session.get("pendingGodPackCards") + finalizeAccount := session.get("pendingGodPackFinalize") + preCapturedScreenshot := session.get("pendingGodPackScreenshot") + session.set("pendingGodPack", false) + session.set("pendingGodPackValidity", "") + session.set("pendingGodPackCards", "") + session.set("pendingGodPackFinalize", false) + session.set("pendingGodPackScreenshot", "") + + GodPackFound(validity, cards, true, preCapturedScreenshot) + + if (validity = "Invalid") + RemoveFriends() + IniWrite, 0, % session.get("scriptIniFile"), UserSettings, DeadCheck + + if (finalizeAccount && validity != "Invalid") + FinalizeInjectedGodPackAccount() + + reason := (validity = "Invalid") ? "Invalid God Pack Found." : "God Pack found. Continuing..." + restartGameInstance(reason, "GodPack") + return true +} + +CheckPack(stopEarly := false) { + prof := Prof_Scope(A_ThisFunc) + expectedOpenedPacks := session.get("expectedPackOpenCount") + 0 + + result := EvaluatePack() + if (!result) { + LogWarn("Failed at 1", "debug_cards.txt") + result := RecoverPack() + } + if (!result) { + LogWarn("Failed at 2", "debug_cards.txt") + ReportPackRecognitionFailure() + if (!stopEarly && expectedOpenedPacks > 1) { + UpdatePackCountAfterOpening() + } + if (!stopEarly && expectedOpenedPacks <= 1) { + CheckPackFallback() + } + return + } + cards := result.cards + pack := result.pack + rarity := result.rarity + shinedust := result.shinedust + raw_msg := result.raw + if(rarity[1] = 0) { + LogWarn(raw_msg, "debug_cards.txt") + ; Fallback in case recognition failed + ReportPackRecognitionFailure() + if (!stopEarly && expectedOpenedPacks > 1) { + UpdatePackCountAfterOpening() + } + if (!stopEarly && expectedOpenedPacks <= 1) { + CheckPackFallback() + } + return + } + + ; store result + LogToCardDatabase(result) + + logMessage := "Instance: " . session.get("scriptName") " | Stored to card database" + LogDebug(logMessage, "debug_cards.txt") + UpdatePackCountAfterOpening() + AddShinedustToDatabase(shinedust) + + if (stopEarly) { + if (botConfig.get("s4tEnabled")) { + CheckCardsSimple(result) + } + return + } + + ; NEW: Disable card detection for Create Bots and Inject 13P+ + ; Only run detection for Inject Wonderpick 96P+ + skipCardDetection := (botConfig.get("deleteMethod") = "Create Bots (13P)" || botConfig.get("deleteMethod") = "Inject 13P+") + multiPullResult := (IsObject(result.pulls) && result.pulls.Length() > 1) || cards.MaxIndex() > 6 + + logMessage := "Instance: " . session.get("scriptName") " | Skip Card Detection: " . skipCardDetection + LogDebug(logMessage, "debug_cards.txt") + + ; If not doing card detection and no friends and s4t disabled, just return early + if(skipCardDetection && !session.get("friendIDs") && botConfig.get("FriendID") = "" && !botConfig.get("s4tEnabled")) + return false + + currentPackIs4Card := cards.MaxIndex() = 4 + currentPackIs6Card := cards.MaxIndex() = 6 + + totalCardsInPack := cards.MaxIndex() + + if (!multiPullResult) { + ; Wait for cards to render before checking. + Loop { + if (CheckCardLoading(totalCardsInPack) = 0) + break + Delay(1) + } + Delay(1) + } + + found1Dmnd := CountOccurances(cards, rarity, 1) + found2Dmnd := CountOccurances(cards, rarity, 2) + found3Dmnd := CountOccurances(cards, rarity, 3) + found4Dmnd := CountOccurances(cards, rarity, 4) + found1Star := CountOccurances(cards, rarity, 7) + foundTrainer := CountOccurances(cards, rarity, 5, "TR_") + foundFullArt := CountOccurances(cards, rarity, 5, "PK_") + foundRainbow := CountOccurances(cards, rarity, 8) + foundImmersive := CountOccurances(cards, rarity, 9) + foundCrown := CountOccurances(cards, rarity, 10) + foundShiny1Star := CountOccurances(cards, rarity, 11) + foundShiny2Star := CountOccurances(cards, rarity, 12) + + PackMethod_UpdateSkipFriendRenewFromCounts(foundImmersive, foundCrown, foundShiny1Star, foundShiny2Star) + + foundWishlist := Wishlist_ProcessPack(cards, pack) + foundWishlist2Star := Wishlist_CountTwoStarMatches(cards, rarity, session.get("wishlistMap")) + + logMessage := "Instance: " . session.get("scriptName") " | Found: " . found1Dmnd + logMessage := logMessage . "|" . found2Dmnd + logMessage := logMessage . "|" . found2Dmnd + logMessage := logMessage . "|" . found3Dmnd + logMessage := logMessage . "|" . found4Dmnd + logMessage := logMessage . "|" . found1Star + logMessage := logMessage . "|" . foundTrainer + logMessage := logMessage . "|" . foundFullArt + logMessage := logMessage . "|" . foundRainbow + logMessage := logMessage . "|" . foundImmersive + logMessage := logMessage . "|" . foundCrown + logMessage := logMessage . "|" . foundShiny1Star + logMessage := logMessage . "|" . foundShiny2Star + LogDebug(logMessage, "debug_cards.txt") + + if (botConfig.get("s4tEnabled")) { + tradeableList := [] + tradeableList.Push({key: "3Diamond", flag: botConfig.get("s4t3Dmnd"), count: found3Dmnd}) + tradeableList.Push({key: "4Diamond", flag: botConfig.get("s4t4Dmnd"), count: found4Dmnd}) + tradeableList.Push({key: "1Star", flag: botConfig.get("s4t1Star"), count: found1Star}) + tradeableList.Push({key: "Trainer", flag: botConfig.get("s4tTrainer"), count: foundTrainer}) + tradeableList.Push({key: "FullArt", flag: botConfig.get("s4tFullArt"), count: foundFullArt}) + tradeableList.Push({key: "Rainbow", flag: botConfig.get("s4tRainbow"), count: foundRainbow}) + tradeableList.Push({key: "Immersive", flag: botConfig.get("s4tImmersive"), count: foundImmersive}) + tradeableList.Push({key: "Crown", flag: botConfig.get("s4tCrown"), count: foundCrown}) + tradeableList.Push({key: "Shiny1Star",flag: botConfig.get("s4tShiny1Star"), count: foundShiny1Star}) + tradeableList.Push({key: "Shiny2Star",flag: botConfig.get("s4tShiny2Star"), count: foundShiny2Star}) + tradeableList.Push({key: "Wishlist", flag: botConfig.get("s4tWishlist"), count: foundWishlist}) + + foundCards := {} + foundTradeable := 0 + for _, item in tradeableList { + foundCards[item.key] := 0 + if (item.flag) { + foundTradeable += item.count + foundCards[item.key] := item.count + } + } + + logMessage := "Instance: " . session.get("scriptName") " | S4T Trandables: " . foundTradeable + LogDebug(logMessage, "debug_cards.txt") + + if (foundTradeable > 0) { + FoundTradeableNew(foundCards, pack, cards, rarity, multiPullResult) + ; Continue with the rest of the run in s4t mode; don't return early. + } + } + + if (multiPullResult) { + return false + } + + ; Skip rest of card detection if this is Create Bots or Inject 13P+ + if (skipCardDetection) { + return false + } + + logMessage := "Instance: " . session.get("scriptName") " | GP Check " + LogDebug(logMessage, "debug_cards.txt") + + foundLabel := false + + ; Check if the current pack is valid (for Inject Wonderpick 96P+ only now) + foundShiny := foundShiny1Star + foundShiny2Star + foundInvalid := foundShiny + foundCrown + foundImmersive + tempStarCount := foundFullArt + foundRainbow + foundTrainer + normalBorders := found1Dmnd + found2Dmnd + found3Dmnd + found4Dmnd + + logMessage := "Instance: " . session.get("scriptName") " | normalBorders: " . normalBorders + LogDebug(logMessage, "debug_cards.txt") + + ; Build currentPackInfo in session so GodPackFound can read starCount correctly + synthPackInfo := {"isVerified": true, "CardSlot": [], "TypeCount": {}} + synthPackInfo["TypeCount"]["normal"] := normalBorders + synthPackInfo["TypeCount"]["1star"] := found1Star + synthPackInfo["TypeCount"]["3diamond"] := found3Dmnd + synthPackInfo["TypeCount"]["trainer"] := foundTrainer + synthPackInfo["TypeCount"]["fullart"] := foundFullArt + synthPackInfo["TypeCount"]["rainbow"] := foundRainbow + synthPackInfo["TypeCount"]["immersive"] := foundImmersive + synthPackInfo["TypeCount"]["crown"] := foundCrown + session.set("currentPackInfo", synthPackInfo) + + if (foundInvalid && botConfig.get("InvalidCheck")) { + logMessage := "Instance: " . session.get("scriptName") " | Invalid" + LogDebug(logMessage, "debug_cards.txt") + ; Skip invalid packs if invalidcheck is active + return + } + + if (foundInvalid) { + logMessage := "Instance: " . session.get("scriptName") " | Doing Invalid Check" + LogDebug(logMessage, "debug_cards.txt") + ; Pack is invalid... + foundInvalidGP := CardDetection_CheckGodPackDeferred(true, cards, false) ; GP is never ignored + + if (foundInvalidGP){ + return + } + if (!foundInvalidGP) { + ; If not a GP and not "ignore invalid packs", check what cards the current pack contains which make it invalid + if (botConfig.get("ShinyCheck") && foundShiny && !foundLabel) + foundLabel := "Shiny" + if (botConfig.get("ImmersiveCheck") && foundImmersive && !foundLabel) + foundLabel := "Immersive" + if (botConfig.get("CrownCheck") && foundCrown && !foundLabel) + foundLabel := "Crown" + + ; Report invalid cards found. + if (foundLabel) { + FoundStars(foundLabel) + restartGameInstance(foundLabel . " found. Continuing...", "GodPack") + } + } + + IniWrite, 0, % session.get("scriptIniFile"), UserSettings, DeadCheck + return + } + + ; Check for god pack. if found we know its not invalid + session.set("foundGP", CardDetection_CheckGodPackDeferred(false, cards, true)) + + if (session.get("foundGP")) { + return + } + + ; Check for 2-star cards (for Inject Wonderpick 96P+ only) + 2starCount := false + + if (botConfig.get("PseudoGodPack") && !foundLabel) { + 2starCount := foundTrainer + foundRainbow + foundFullArt + if (2starCount > 1) + foundLabel := "Double two star" + } + if (botConfig.get("TrainerCheck") && !foundLabel && foundTrainer) { + foundLabel := "Trainer" + } + if (botConfig.get("RainbowCheck") && !foundLabel && foundRainbow) { + foundLabel := "Rainbow" + } + if (botConfig.get("FullArtCheck") && !foundLabel && foundFullArt) { + foundLabel := "Full Art" + } + if (botConfig.get("WishlistCheck") && !foundLabel && foundWishlist2Star) { + session.set("wishlistMatches", Wishlist_TwoStarMatchEntries(cards, rarity, session.get("wishlistMap"))) + foundLabel := "Wishlist" + } + + if (foundLabel) { + FinalizeInjectedGodPackAccount() + FoundStars(foundLabel, cards) + restartGameInstance(foundLabel . " found. Continuing...", "GodPack") + } + +} + +CheckPackFallback() { + prof := Prof_Scope(A_ThisFunc) + global botConfig, session + + currentPackIs6Card := false ; reset before each pack check + currentPackIs6Card := false ; reset before each pack check + + UpdatePackCountAfterOpening() + + ; NEW: Disable card detection for Create Bots and Inject 13P+ + ; Only run detection for Inject Wonderpick 96P+ + skipCardDetection := (botConfig.get("deleteMethod") = "Create Bots (13P)" || botConfig.get("deleteMethod") = "Inject 13P+") + + ; If not doing card detection and no friends and s4t disabled, just return early + if(skipCardDetection && !session.get("friendIDs") && botConfig.get("FriendID") = "" && !botConfig.get("s4tEnabled")) + return false + + currentPackIs4Card := DetectFourCardPack() + if (!currentPackIs4Card) { + currentPackIs6Card := DetectSixCardPack() + } + + ; Determine total cards in pack for 4-diamond s4t calculations + totalCardsInPack := currentPackIs6Card ? 6 : (currentPackIs4Card ? 4 : 5) + + ; Wait for cards to render before checking. + Loop { + if (CheckCardLoading(totalCardsInPack) = 0) + break + Delay(1) + } + Delay(1) + + currentPackInfo := AnalysisBorder(totalCardsInPack) + session.set("currentPackInfo", currentPackInfo) + + ; NEW: Check for s4t tradeable cards FIRST (before invalid/godpack checks) + ; This allows s4t to work even on "invalid" packs with crowns/immersives/etc + if (botConfig.get("s4tEnabled")) { + found3Dmnd := 0 + found4Dmnd := 0 + found1Star := 0 + foundGimmighoul := 0 + foundCrown := 0 + foundImmersive := 0 + foundShiny1Star := 0 + foundShiny2Star := 0 + foundTrainer := 0 + foundRainbow := 0 + foundFullArt := 0 + + ; Check all border types for s4t (only if enabled) + if (botConfig.get("s4t3Dmnd")) { + found3Dmnd := currentPackInfo["TypeCount"]["3diamond"] + } + if (botConfig.get("s4t1Star")) { + found1Star := currentPackInfo["TypeCount"]["1star"] + } + if (botConfig.get("s4t4Dmnd")) { + ; Detecting a 4-diamond EX card by subtracting other types + found4Dmnd := totalCardsInPack - currentPackInfo["TypeCount"]["normal"] + if (found4Dmnd > 0) { + if (botConfig.get("s4t3Dmnd")) + found4Dmnd -= found3Dmnd + else + found4Dmnd -= currentPackInfo["TypeCount"]["3diamond"] + } + if (found4Dmnd > 0) { + if (botConfig.get("s4t1Star")) + found4Dmnd -= found1Star + else + found4Dmnd -= currentPackInfo["TypeCount"]["1star"] + } + if (found4Dmnd > 0) { + found4Dmnd -= currentPackInfo["TypeCount"]["trainer"] + found4Dmnd -= currentPackInfo["TypeCount"]["rainbow"] + found4Dmnd -= currentPackInfo["TypeCount"]["fullart"] + found4Dmnd -= currentPackInfo["TypeCount"]["immersive"] + found4Dmnd -= currentPackInfo["TypeCount"]["crown"] + found4Dmnd -= currentPackInfo["TypeCount"]["ShinyEx"] + found4Dmnd -= currentPackInfo["TypeCount"]["shiny1star"] + } + } + if (botConfig.get("s4tGholdengo") && session.get("openPack") = "Shining") { + foundGimmighoul := FindCard("gimmighoul") + } + + ; NEW: Only check if the specific card type is enabled + if (botConfig.get("s4tCrown")) { + foundCrown := currentPackInfo["TypeCount"]["crown"] + } + if (botConfig.get("s4tImmersive")) { + foundImmersive := currentPackInfo["TypeCount"]["immersive"] + } + if (botConfig.get("s4tShiny2Star")) { + foundShiny2Star := currentPackInfo["TypeCount"]["ShinyEx"] + } + if (botConfig.get("s4tShiny1Star")) { + foundShiny1Star := currentPackInfo["TypeCount"]["shiny1star"] + } + if (botConfig.get("s4tTrainer")) { + foundTrainer := currentPackInfo["TypeCount"]["trainer"] + } + if (botConfig.get("s4tRainbow")) { + foundRainbow := currentPackInfo["TypeCount"]["rainbow"] + } + if (botConfig.get("s4tFullArt")) { + foundFullArt := currentPackInfo["TypeCount"]["fullart"] + } + + foundTradeable := found3Dmnd + found4Dmnd + found1Star + foundGimmighoul + foundCrown + foundImmersive + foundShiny1Star + foundShiny2Star + foundTrainer + foundRainbow + foundFullArt + + if (foundTradeable > 0) { + FoundTradeable(found3Dmnd, found4Dmnd, found1Star, foundGimmighoul, foundCrown, foundImmersive, foundShiny1Star, foundShiny2Star, foundTrainer, foundRainbow, foundFullArt) + ; Continue with the rest of the run in s4t mode; don't return early. + } + } + + ; Skip rest of card detection if this is Create Bots or Inject 13P+ + if (skipCardDetection) { + return false + } + + foundLabel := false + + ; Check if the current pack is valid (for Inject Wonderpick 96P+ only now) + foundShiny := currentPackInfo["TypeCount"]["ShinyEx"] + currentPackInfo["TypeCount"]["shiny1star"] + foundCrown := currentPackInfo["TypeCount"]["crown"] + foundImmersive := currentPackInfo["TypeCount"]["immersive"] + foundInvalid := foundShiny + foundCrown + foundImmersive + + if (foundInvalid) { + ; Pack is invalid... + foundInvalidGP := CardDetection_CheckGodPackDeferred(true, "", false) ; GP is never ignored + + if (foundInvalidGP){ + return + } + if (!foundInvalidGP && !botConfig.get("InvalidCheck")) { + ; If not a GP and not "ignore invalid packs", check what cards the current pack contains which make it invalid + if (botConfig.get("ShinyCheck") && foundShiny && !foundLabel) + foundLabel := "Shiny" + if (botConfig.get("ImmersiveCheck") && foundImmersive && !foundLabel) + foundLabel := "Immersive" + if (botConfig.get("CrownCheck") && foundCrown && !foundLabel) + foundLabel := "Crown" + + ; Report invalid cards found. + if (foundLabel) { + FoundStars(foundLabel) + restartGameInstance(foundLabel . " found. Continuing...", "GodPack") + } + } + + IniWrite, 0, % session.get("scriptIniFile"), UserSettings, DeadCheck + return + } + + ; Check for god pack. if found we know its not invalid + session.set("foundGP", CardDetection_CheckGodPackDeferred(false, "", true)) + + if (session.get("foundGP")) { + return + } + + ; Check for 2-star cards (for Inject Wonderpick 96P+ only) + foundTrainer := false + foundRainbow := false + foundFullArt := false + 2starCount := false + + if (botConfig.get("PseudoGodPack") && !foundLabel) { + foundTrainer := currentPackInfo["TypeCount"]["trainer"] + foundRainbow := currentPackInfo["TypeCount"]["rainbow"] + foundFullArt := currentPackInfo["TypeCount"]["fullart"] + 2starCount := foundTrainer + foundRainbow + foundFullArt + if (2starCount > 1) + foundLabel := "Double two star" + } + if (botConfig.get("TrainerCheck") && !foundLabel) { + if(!botConfig.get("PseudoGodPack")) + foundTrainer := currentPackInfo["TypeCount"]["trainer"] + if (foundTrainer) + foundLabel := "Trainer" + } + if (botConfig.get("RainbowCheck") && !foundLabel) { + if(!botConfig.get("PseudoGodPack")) + foundRainbow := currentPackInfo["TypeCount"]["rainbow"] + if (foundRainbow) + foundLabel := "Rainbow" + } + if (botConfig.get("FullArtCheck") && !foundLabel) { + if(!botConfig.get("PseudoGodPack")) + foundFullArt := currentPackInfo["TypeCount"]["fullart"] + if (foundFullArt) + foundLabel := "Full Art" + } + + if (foundLabel) { + FinalizeInjectedGodPackAccount() + FoundStars(foundLabel) + restartGameInstance(foundLabel . " found. Continuing...", "GodPack") + } +} diff --git a/Scripts/Include/BotPackOpening.ahk b/Scripts/Include/BotPackOpening.ahk new file mode 100644 index 00000000..8192bdf8 --- /dev/null +++ b/Scripts/Include/BotPackOpening.ahk @@ -0,0 +1,1025 @@ +; Pack selection and opening flow, gifts and hourglass opening + +ensureMissionUserPrefsExist() { + global session + if(!doesMissionUserPrefsExist()) { + session.set("failSafe", A_TickCount) + failSafeTime := 0 + ; Click for hamburger menu and wait for profile + Loop { + adbClick(240, 494) + if(FindOrLoseImage("Profile_UserNameArrowInSettingMenu", 0, failSafeTime)) { + break + } else { + clickButton := FindOrLoseImage("Common_ColorChangeButton", 0, , 80) + if(clickButton) { + StringSplit, pos, clickButton, `, ; Split at ", " + adbClick(pos1, pos2) + } + } + Delay(1) + failSafeTime := (A_TickCount - failSafe) // 1000 + } + + FindImageAndClick("Profile_EditNameButtonIcon", 210, 140, , 200) ; Open profile/stats page and wait + session.set("failSafe", A_TickCount) + failSafeTime := 0 + ; Click for hamburger menu and wait for profile + Loop { + adbInputEvent("111") ;send ESC + if(FindOrLoseImage("Profile_UserNameArrowInSettingMenu", 0, failSafeTime)) { + break + } else { + clickButton := FindOrLoseImage("Common_ColorChangeButton", 0, , 80) + if(clickButton) { + StringSplit, pos, clickButton, `, ; Split at ", " + adbClick(pos1, pos2) + } + } + Delay(1) + failSafeTime := (A_TickCount - failSafe) // 1000 + } + + } +} + +FindHourglassOpenConfirmation(tenPackOpening, failSafeTime) { + if (tenPackOpening) + return (FindOrLoseImage(67, 446, 83, 468, , "HourglassPack10", 0, failSafeTime) || FindOrLoseImage(45, 446, 60, 465, , "HourGlassAndPokeGoldPack10", 0, failSafeTime) || FindOrLoseImage("Pack_PokeGoldImageAfterOpenPackClick", 0, failSafeTime) || FindOrLoseImage(66, 447, 84, 465, , "PokeGoldPackNoHourglasses", 0, failSafeTime)) + + return (FindOrLoseImage("Pack_HourglassImageAfterOpenPackClick", 0, failSafeTime) || FindOrLoseImage("Pack_HourglassAndPokeGoldImageAfterOpenPackClick", 0, failSafeTime) || FindOrLoseImage("Pack_PokeGoldImageAfterOpenPackClick", 0, failSafeTime) || FindOrLoseImage(66, 447, 84, 465, , "PokeGoldPackNoHourglasses", 0, failSafeTime)) +} + +FindHourglassOpenConfirmationClosed(tenPackOpening, failSafeTime) { + if (tenPackOpening) + return (FindOrLoseImage(67, 446, 83, 468, , "HourglassPack10", 1, failSafeTime) && FindOrLoseImage(45, 446, 60, 465, , "HourGlassAndPokeGoldPack10", 1, failSafeTime) && FindOrLoseImage("Pack_PokeGoldImageAfterOpenPackClick", 1, failSafeTime) && FindOrLoseImage(66, 447, 84, 465, , "PokeGoldPackNoHourglasses", 1, failSafeTime)) + + return (FindOrLoseImage("Pack_HourglassImageAfterOpenPackClick", 1, failSafeTime) && FindOrLoseImage("Pack_HourglassAndPokeGoldImageAfterOpenPackClick", 1, failSafeTime) && FindOrLoseImage("Pack_PokeGoldImageAfterOpenPackClick", 1, failSafeTime) && FindOrLoseImage(66, 447, 84, 465, , "PokeGoldPackNoHourglasses", 1, failSafeTime)) +} + +WaitForPackPointButtonFromHome(clickX, clickY, context := "") { + global session + + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + if(DismissMainCloseAlertWindow(context)) { + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + continue + } + + adbClick_wbb(clickX, clickY) + Delay(0.5) + if(FindOrLoseImage("Pack_PackPointButton", 0, failSafeTime)) + return true + + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting for Points`n(" . failSafeTime . "/90 seconds)") + } +} + +;------------------------------------------------------------------------------- +; EnterFavouritePackFromHome - switch to favourites view in Home, click the +; pack once to enter the Points screen, then wait for Pack_PackPointButton. +;------------------------------------------------------------------------------- +EnterFavouritePackFromHome() { + global session + + session.set("favEnteredFromHome", true) + + ; Skip favourites switch for packs already stable in Home. + mainScreenPacks := session.get("mainScreenPackList") + isStableInHome := false + homePosition := "" + if (IsObject(mainScreenPacks)) { + for pos, packName in mainScreenPacks { + if (packName = session.get("openPack")) { + isStableInHome := true + homePosition := pos + break + } + } + } + + if (!isStableInHome) { + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + adbClick_wbb(265, 236) + Delay(1) + if (FindOrLoseImage(245, 146, 251, 153, , "FavouriteBooster", 0, failSafeTime)) + break + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + if (failSafeTime >= 30) { + LogWarn("EnterFavouritePackFromHome: timed out waiting for FavouriteBooster", "ADB.txt") + break + } + } + } + + ; For stable Home packs, use the Home position (Left/Middle/Right). + ; For favourite packs, use GetPackFavoriteHomeX (position within expansion). + mapHomeX := {"Left":60, "Middle":140, "Right":215} + if (isStableInHome) { + favHomeX := mapHomeX[homePosition] + if (favHomeX = "") + favHomeX := 140 + } else if (IsFunc("GetPackFavoriteHomeX")) { + favHomeX := GetPackFavoriteHomeX(session.get("openPack")) + } else { + favHomeX := 140 + } + + WaitForPackPointButtonFromHome(favHomeX, 203, "after fav pack select") +} + +RecoverPackOpeningToMainIfNeeded(caller := "") { + global session + + foundRecoveryScreen := false + + if (FindOrLoseImage("Common_AlertForAppCrachDuringOpenPack", 0, 0, , true)) { + adbClick_wbb(139, 371) + Delay(2) + foundRecoveryScreen := true + } + + if (!foundRecoveryScreen) { + foundRecoveryScreen := FindOrLoseImage("Create_NintendoLink", 0, 0, , true) + if (!foundRecoveryScreen) + foundRecoveryScreen := FindOrLoseImage("Create_DownloadAlertWindow", 0, 0, , true) + if (!foundRecoveryScreen) + foundRecoveryScreen := FindOrLoseImage("Create_DownloadComplete", 0, 0, , true) + if (!foundRecoveryScreen) + foundRecoveryScreen := FindOrLoseImage("Create_CinematicBackground", 0, 0, , true) + if (!foundRecoveryScreen) + foundRecoveryScreen := FindOrLoseImage("Create_WelcomePopup", 0, 0, , true) + if (!foundRecoveryScreen) + foundRecoveryScreen := FindOrLoseImage("StartupErrorX", 0, 0, , true) + if (!foundRecoveryScreen) + foundRecoveryScreen := FindOrLoseImage("Common_ShopButtonInMain", 0, 0, , true) + } + + if (!foundRecoveryScreen) + return false + + LogInfo("Pack opening error recovery from " . caller . ": returning to selected pack screen", "Restart.txt") + session.set("failSafe", A_TickCount) + failSafeTime := 0 + + Loop { + if (FindOrLoseImage("Common_ShopButtonInMain", 0, 0, , true)) { + GoToMain() + return true + } + + if (FindOrLoseImage("Common_AlertForAppCrachDuringOpenPack", 0, 0, , true)) { + adbClick_wbb(139, 371) + } else if (FindOrLoseImage("StartupErrorX", 0, 0, , true)) { + adbClick_wbb(140, 439) + } else if (FindOrLoseImage("Create_DownloadAlertWindow", 0, 0, , true)) { + adbClick_wbb(203, 364) + } else if (FindOrLoseImage("Create_DownloadComplete", 0, 0, , true)) { + adbClick_wbb(140, 370) + } else if (FindOrLoseImage("Create_NintendoLink", 0, 0, , true)) { + adbClick_wbb(140, 460) + } else if (FindOrLoseImage("Create_WelcomePopup", 0, 0, , true)) { + adbClick_wbb(253, 506) + } else if (FindOrLoseImage("Create_CinematicBackground", 0, 0, , true)) { + adbClick_wbb(253, 506) + } else { + adbClick_wbb(140, 460) + } + + Delay(1) + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Recovering pack opening`n(" . failSafeTime . "/90 seconds)") + if (failSafeTime > 90) { + restartGameInstance("Stuck recovering pack opening error") + return false + } + } +} + +SelectPack(HG := false) { + global session + + if(HG = "HGPack" || HG = "HGPack10") + session.set("packOpeningRecoveryPack", HG) + else + session.set("packOpeningRecoveryPack", "") + + ; define constants + mapPackX := {"Left":60, "Middle":140, "Right":215} + mainScreenPackCoords := {"MegaBlaziken":mapPackX["Left"], "Parade":mapPackX["Middle"], "CrimsonBlaze":mapPackX["Right"]} + + HomeScreenAllPackY := 203 + PackScreenAllPackY := 320 + + packx := getPackCoordXInHome() + packy := HomeScreenAllPackY + enteredPackScreenFromHome := false + + LogInfo("SelectPack: HG=" . HG . " packFavoriteSet=" . session.get("packFavoriteSet") . " isSkipSelectExpansion=" . session.get("isSkipSelectExpansion") . " openPack=" . session.get("openPack"), "ADB.txt") + + ; When favourite pack is set and we are coming from Home (not First boot), + ; switch to the favourites view in Home before clicking the pack. + ; Skip the switch for packs that are already stable in the Home screen + ; (listed in mainScreenPackList). + if (session.get("packFavoriteSet") && HG != "First") { + mainScreenPacks := session.get("mainScreenPackList") + isStableInHome := false + homePosition := "" + if (IsObject(mainScreenPacks)) { + for pos, packName in mainScreenPacks { + if (packName = session.get("openPack")) { + isStableInHome := true + homePosition := pos + break + } + } + } + if (!isStableInHome) { + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + adbClick_wbb(265, 236) + Delay(1) + if (FindOrLoseImage(245, 146, 251, 153, , "FavouriteBooster", 0, failSafeTime)) + break + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + if (failSafeTime >= 30) { + LogWarn("SelectPack: timed out waiting for FavouriteBooster in Home", "ADB.txt") + break + } + } + } + ; For stable Home packs, use the Home position (Left/Middle/Right). + ; For favourite packs, use GetPackFavoriteHomeX (position within expansion). + if (isStableInHome) { + packx := mapPackX[homePosition] + if (packx = "") + packx := 140 + } else if (IsFunc("GetPackFavoriteHomeX")) { + packx := GetPackFavoriteHomeX(session.get("openPack")) + } + } + + ensureMissionUserPrefsExist() + InitPackOpening() + if(HG = "First" && session.get("injectMethod") && session.get("loadedAccount") ){ + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + if(DismissMainCloseAlertWindow("Waiting for Points")) { + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + continue + } + + ; When favourite pack is set, the game boots directly into the + ; Points screen. Don't click in Home, just wait for Points. + if (!session.get("packFavoriteSet")) + adbClick_wbb(packx, HomeScreenAllPackY) + Delay(1) + if(FindOrLoseImage("Pack_PackPointButton", 0, failSafeTime)) { + break + } + else if(!renew && !getFC) { + if(FindOrLoseImage("Common_AlertForAppCrachDuringOpenPack", 0)) { + adbClick_wbb(139, 371) + } + } + else if(FindOrLoseImage("Create_TutorialUseResourceForOpenPack", 0)) { + ;TODO hourglass tutorial still broken after injection + Delay(3) + adbClick_wbb(146, 441) + Delay(3) + adbClick_wbb(146, 441) + Delay(3) + adbClick_wbb(146, 441) + Delay(3) + + FindImageAndClick("Create_TutorialPremiumPass", 168, 438, , 500, 5) ;stop at hourglasses tutorial 2 + Delay(1) + + adbClick_wbb(203, 436) + FindImageAndClick("Create_InfoIconInStandByOpenPack", 180, 436, , 500) ;stop at hourglasses tutorial 2 180 to 203? + } + + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting for Points`n(" . failSafeTime . "/90 seconds)") + } + enteredPackScreenFromHome := true + } + + if (!enteredPackScreenFromHome) + FindImageAndClick("Pack_PackPointButton", packx, packy, , 1000) + + if(!session.get("isSkipSelectExpansion")) { + FindImageAndClick("Pack_ScrollInSelectExpansion", 248, 459, , 300) + + ; packs that can be opened after clicking A series + session.get("packCoordinates")[session.get("openPack")].moveSeriesScreen() + session.get("packCoordinates")[session.get("openPack")].expansionScreenDrag() + + packx := session.get("packCoordinates")[session.get("openPack")].getXPos() + packy := session.get("packCoordinates")[session.get("openPack")].getYPos() + + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop{ + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Move pack:" . session.get("openPack") . "`n(" . failSafeTime . "/90 seconds)") + + adbClick_wbb(packx, packy) + Delay(2) + if(FindOrLoseImage("Pack_ScrollInSelectExpansion", 1, , , 1)) { + break + } + } + Delay(2) + session.get("packCoordinates")[session.get("openPack")].additionalAction() + } + else if (session.get("packFavoriteSet")) { + ; Favourite pack: already on the correct expansion screen. + ; Nothing to do here. The pack selection click happens after + ; FindPackStats() below, so we don't interfere with it. + } + + if(HG = "First" && session.get("injectMethod") && session.get("loadedAccount") && !session.get("accountHasPackInfo")) { + FindPackStats() + } + + ; Favourite pack: now that FindPackStats is done (or skipped), click the + ; specific pack within the expansion to select it. Only needed on cold + ; boot (game boots with centre pack in foreground). When entering from + ; Home favourites, the clicked pack is already in foreground. + if (session.get("packFavoriteSet") && HG = "First" && !session.get("favEnteredFromHome")) { + if (IsFunc("GetPackFavoritePointsX")) + favPackX := GetPackFavoritePointsX(session.get("openPack")) + else + favPackX := 140 + if (favPackX != 140) { + adbClick_wbb(favPackX, PackScreenAllPackY) + Delay(1) + } + } + + if(HG = "Tutorial") { + FindImageAndClick("Create_InfoIconInStandByOpenPack", 180, 436, , 500) ;stop at hourglasses tutorial 2 180 to 203? + } + else if(HG = "HGPack" || HG = "HGPack10") { + tenPackOpening := (HG = "HGPack10") + openPackCoord := session.get("packCoordinates")[session.get("openPack")] + if (IsObject(openPackCoord)) + openPackCoord.tapPackPreviewUntilPointsGone() + Delay(1) + + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + if (RecoverPackOpeningToMainIfNeeded("SelectPackConfirmation")) { + SelectPack(HG) + return + } + if(FindHourglassOpenConfirmation(tenPackOpening, failSafeTime)) { + break + }else if(FindOrLoseImage("Pack_NotEnoughItemsForOpenPack", 0)) { + session.set("cantOpenMorePacks", 1) + } + if(session.get("cantOpenMorePacks")) + return + openButtonX := tenPackOpening ? 70 : 161 + adbClick_wbb(openButtonX, 423) + Delay(1) + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + statusText := tenPackOpening ? "Waiting for HourglassPack10" : "Waiting for HourglassPack3" + CreateStatusMessage(statusText . "`n(" . failSafeTime . "/45 seconds)") + } + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + if (RecoverPackOpeningToMainIfNeeded("SelectPackConfirmationClosed")) { + SelectPack(HG) + return + } + if(FindHourglassOpenConfirmationClosed(tenPackOpening, failSafeTime)) { + break + } + adbClick_wbb(205, 458) + Delay(1) + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting for HourglassPack4`n(" . failSafeTime . "/45 seconds)") + } + } else { + openPackCoord := session.get("packCoordinates")[session.get("openPack")] + if (IsObject(openPackCoord)) + openPackCoord.tapPackPreviewUntilPointsGone() + Delay(1) + + session.set("failSafe", A_TickCount) + failSafeTime := 0 + failsafeClickExecuted := false ; Flag to track if failsafe click has been executed + Loop { + adbClick_wbb(151, 420) ; open button + if(FindOrLoseImage("Pack_AnimationToReadyOpenPack", 0, failSafeTime)) { + break + } else if(FindOrLoseImage("Pack_NotEnoughItemsForOpenPack", 0)) { + session.set("cantOpenMorePacks", 1) + } else if(FindOrLoseImage("Pack_HourglassImageAfterOpenPackClick", 0, 1) || FindOrLoseImage("Pack_HourglassAndPokeGoldImageAfterOpenPackClick", 0, 1)) { + adbClick_wbb(205, 458) ; Handle unexpected HG pack confirmation + } else if(FindOrLoseImage("Common_AlertForAppCrachDuringOpenPack", 0)) { + ; Handle restart caused due to network error + adbClick_wbb(139, 371) + if (session.get("injectMethod") && session.get("loadedAccount") && session.get("friended")) { + IniWrite, 1, % session.get("scriptIniFile"), UserSettings, DeadCheck + } + LogInfo("[" . A_ScriptName . "] Stuck #1 in SelectPack.", "Restart.txt") + restartGameInstance("Stuck at pack opening") + return + } else { + adbClick_wbb(200, 451) ; Additional fallback click + } + + if(session.get("cantOpenMorePacks")) + return + + Delay(0.1) + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting for Skip2`n(" . failSafeTime . "/45 seconds)") + } + } +} + +PackOpening(tenPackOpening := false) { + global session + if (isTerminatePTCGPHelperApp()) { + InitPackOpening() + } + recoveryPack := tenPackOpening ? "HGPack10" : session.get("packOpeningRecoveryPack") + session.set("failSafe", A_TickCount) + failSafeTime := 0 + failsafeClickExecuted := false + Loop { + if(recoveryPack != "" && RecoverPackOpeningToMainIfNeeded("PackOpening")) { + SelectPack(recoveryPack) + if(session.get("cantOpenMorePacks")) + return + session.set("failSafe", A_TickCount) + failSafeTime := 0 + failsafeClickExecuted := false + continue + } + ; 2nd+ pack: game may force Welcome Back missions tab while Waiting for Pack + if(TryRecoverWelcomeBackMissionsAfterPack("PackOpening", recoveryPack)) { + session.set("failSafe", A_TickCount) + failSafeTime := 0 + failsafeClickExecuted := false + continue + } + adbClick_wbb(146, 434) + Delay(0.2) + adbClick_wbb(170, 455) + if(FindOrLoseImage("Pack_ReadyForOpenPack", 0, failSafeTime)) { + break ;wait for pack to be ready to Trace and click skip + } else if(FindOrLoseImage("Pack_NotEnoughItemsForOpenPack", 0)) { + session.set("cantOpenMorePacks", 1) + } else if(FindOrLoseImage("Pack_HourglassImageAfterOpenPackClick", 0, 1) || FindOrLoseImage("Pack_HourglassAndPokeGoldImageAfterOpenPackClick", 0, 1)) { + adbClick_wbb(205, 453) ; handle unexpected no packs available + } else if(FindOrLoseImage("Pack_GetItemDialogAfterOpenPack", 0)){ + adbInputEvent("111") + Delay(2) + } else { + adbClick_wbb(239, 492) + } + + ; Execute failsafe click only once after 10 seconds to try to select Floating Pack + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + if (failSafeTime >= 10 && !failsafeClickExecuted) { + if (FindOrLoseImage("Pack_PackPointButton", 0)) { + CreateStatusMessage("Trying to click floating pack...") + Sleep, 3000 + adbClick_wbb(151, 245) ; if pack is floating/glitched + failsafeClickExecuted := true + } + } + + if(session.get("cantOpenMorePacks")) + return + + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting for Pack`n(" . failSafeTime . "/45 seconds)") + if(failSafeTime > 45){ + RemoveFriends() + if(session.get("injectMethod") && session.get("loadedAccount") && session.get("friended")) { + IniWrite, 1, % session.get("scriptIniFile"), UserSettings, DeadCheck + } + restartGameInstance("Stuck at Pack") + } + } + + if(session.get("setSpeed") > 1) { + FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) + FindImageAndClick(GetSpeedModNeedle(1), GetSpeedModClickX(1), GetSpeedModClickY(1)) + Delay(1) + adbClick_wbb(51, 297) + Delay(1) + } + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + adbSwipe_wbb(adbSwipeParams) + Sleep, 100 + if (FindOrLoseImage("Pack_ReadyForOpenPack", 1, failSafeTime)){ + if(session.get("setSpeed") > 1) { + if(session.get("setSpeed") = 3) { + FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) + FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) + } + } + adbClick_wbb(51, 292) + break + } + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting for Trace`n(" . failSafeTime . "/45 seconds)") + Delay(1) + } + + resultNeedle := tenPackOpening ? "Gift_ResultAfterOpenPack" : "Pack_ResultAfterOpenPack" + FindImageAndClick(resultNeedle, 252, 505, 5, 25) ;skip through cards until results opening screen + + CheckPack() + SetLastPackPulledNow() + ; A completed pack is forward progress even when the overall account task + ; runs longer than the monitor threshold. + writeLastActivityEpoch(session.get("scriptName")) + + if(!CardDetection_HasPendingGodPack() && !session.get("friendIDs") && botConfig.get("FriendID") = "" && session.get("accountOpenPacks") >= session.get("maxAccountPackNum")) + return + + ;FindImageAndClick("Pack_SkipButtonAfterOpenPack", 146, 494) ;click on next until skip button appears + + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + Delay(4) + if(FindOrLoseImage("Pack_SkipButtonAfterOpenPack", 0, failSafeTime)) { + adbClick_wbb(247, 500) + Delay(1) + } else if(FindOrLoseImage("Pack_NextButtonAfterOpenPack", 0, failSafeTime)) { + adbClick_wbb(146, 489) ;146, 494 + Delay(1) + } else if(FindOrLoseImage("Next2", 0, failSafeTime)) { + adbClick_wbb(146, 489) ;146, 494 + Delay(1) + } else if(FindOrLoseImage("Pack_BackButtonInSelectPackScreen", 0, failSafeTime)) { + break + } else if(FindOrLoseImage("Create_TutorialUseResourceForOpenPack", 0, failSafeTime)) { + break + } else { + adbClick_wbb(146, 489) ;146, 494 + Delay(1) + } + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting for Home`n(" . failSafeTime . "/45 seconds)") + if(failSafeTime > 45) + restartGameInstance("Stuck at Home") + } + + CardDetection_FlushPendingGodPack() +} + +HourglassOpening(HG := false, NEIRestart := true, tenPackOpening := false) { + global botConfig, session + if (isTerminatePTCGPHelperApp()) { + InitPackOpening() + } + recoveryPack := tenPackOpening ? "HGPack10" : "HGPack" + if(!HG) { + Delay(3) + adbClick_wbb(146, 441) ; 146 440 + Delay(3) + adbClick_wbb(146, 441) + Delay(3) + adbClick_wbb(146, 441) + Delay(3) + + FindImageAndClick("Create_TutorialPremiumPass", 168, 430, , 650, 5) ;stop at hourglasses tutorial 2 + Delay(1) + + adbClick_wbb(203, 436) ; 203 436 + + if(session.get("packMethod")) { + PackMethod_RenewFriends() + SelectPack("Tutorial") + } + else { + FindImageAndClick("Create_InfoIconInStandByOpenPack", 180, 436, , 500) ;stop at hourglasses tutorial 2 180 to 203? + + if(session.get("cantOpenMorePacks")) + return + } + } + if(!session.get("packMethod")) { + session.set("failSafe", A_TickCount) + failSafeTime := 0 + failsafeClickExecuted := false + recoveredPackOpening := false + Loop { + if (RecoverPackOpeningToMainIfNeeded("HourglassOpeningConfirmation")) { + SelectPack(recoveryPack) + if(session.get("cantOpenMorePacks")) + return + recoveredPackOpening := true + break + } + if(TryRecoverWelcomeBackMissionsAfterPack("HourglassOpening", recoveryPack)) { + session.set("failSafe", A_TickCount) + failSafeTime := 0 + failsafeClickExecuted := false + continue + } + if(FindHourglassOpenConfirmation(tenPackOpening, failSafeTime)) { + break + }else if(FindOrLoseImage("Pack_NotEnoughItemsForOpenPack", 0)) { + session.set("cantOpenMorePacks", 1) + } + if(session.get("cantOpenMorePacks")) + return + + ; Execute failsafe click only once after 10 seconds to try to click floating pack + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + if (failSafeTime >= 10 && !failsafeClickExecuted) { + if (FindOrLoseImage("Pack_PackPointButton", 0)) { + CreateStatusMessage("Trying to click floating pack...") + Sleep, 3000 + adbClick_wbb(151, 250) ; if pack is floating/glitched + failsafeClickExecuted := true + } + } + + if(failSafeTime >= 45) { + restartGameInstance("Stuck waiting for HourglassPack") + return + } + openButtonX := tenPackOpening ? 70 : 146 + adbClick_wbb(openButtonX, 434) + Delay(1) + statusText := tenPackOpening ? "Waiting for HourglassPack10" : "Waiting for HourglassPack" + CreateStatusMessage(statusText . "`n(" . failSafeTime . "/45 seconds)") + } + if(!recoveredPackOpening) { + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + if (RecoverPackOpeningToMainIfNeeded("HourglassOpeningConfirmationClosed")) { + SelectPack(recoveryPack) + if(session.get("cantOpenMorePacks")) + return + recoveredPackOpening := true + break + } + if(FindHourglassOpenConfirmationClosed(tenPackOpening, failSafeTime)) { + break + } + adbClick_wbb(205, 458) + Delay(1) + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting for HourglassPack2`n(" . failSafeTime . "/45 seconds)") + } + } + } + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + if(RecoverPackOpeningToMainIfNeeded("HourglassOpeningPackReady")) { + SelectPack(recoveryPack) + if(session.get("cantOpenMorePacks")) + return + session.set("failSafe", A_TickCount) + failSafeTime := 0 + continue + } + adbClick_wbb(146, 434) + Delay(1) + adbClick_wbb(170, 455) + if(FindOrLoseImage("Pack_ReadyForOpenPack", 0, failSafeTime)) + break ;wait for pack to be ready to Trace and click skip + else + adbClick_wbb(239, 497) + + if(session.get("cantOpenMorePacks")) + return + + if(FindOrLoseImage("Common_ShopButtonInMain", 0, failSafeTime)){ + SelectPack(recoveryPack) + session.set("failSafe", A_TickCount) + failSafeTime := 0 + } + + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting for Pack`n(" . failSafeTime . "/45 seconds)") + if(failSafeTime > 45) { + if(session.get("injectMethod") && session.get("loadedAccount") && session.get("friended")) { + IniWrite, 1, % session.get("scriptIniFile"), UserSettings, DeadCheck + } + restartGameInstance("Stuck at Pack") + } + } + + if(session.get("setSpeed") > 1) { + FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) + FindImageAndClick(GetSpeedModNeedle(1), GetSpeedModClickX(1), GetSpeedModClickY(1)) + Delay(1) + adbClick_wbb(51, 297) + Delay(1) + } + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + adbSwipe_wbb(adbSwipeParams) + Sleep, 100 + if (FindOrLoseImage("Pack_ReadyForOpenPack", 1, failSafeTime)){ + if(session.get("setSpeed") > 1) { + if(session.get("setSpeed") = 3) { + FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) + FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) + } + } + adbClick_wbb(51, 297) + break + } + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting for Trace`n(" . failSafeTime . "/45 seconds)") + Delay(1) + } + + resultNeedle := tenPackOpening ? "Gift_ResultAfterOpenPack" : "Pack_ResultAfterOpenPack" + FindImageAndClick(resultNeedle, 252, 505, 5, 25) ;skip through cards until results opening screen + + CheckPack() + SetLastPackPulledNow() + ; A completed pack is forward progress even when the overall account task + ; runs longer than the monitor threshold. + writeLastActivityEpoch(session.get("scriptName")) + + if(!CardDetection_HasPendingGodPack() && !session.get("friendIDs") && botConfig.get("FriendID") = "" && session.get("accountOpenPacks") >= session.get("maxAccountPackNum")) + return + + ;FindImageAndClick("Pack_SkipButtonAfterOpenPack", 146, 494) ;click on next until skip button appears + + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + Delay(4) + if(FindOrLoseImage("Pack_SkipButtonAfterOpenPack", 0, failSafeTime)) { + adbClick_wbb(239, 497) + Delay(1) + } else if(FindOrLoseImage("Pack_NextButtonAfterOpenPack", 0, failSafeTime)) { + adbClick_wbb(146, 494) ;146, 494 + Delay(1) + } else if(FindOrLoseImage("Next2", 0, failSafeTime)) { + adbClick_wbb(146, 494) ;146, 494 + Delay(1) + } else if(FindOrLoseImage("Pack_BackButtonInSelectPackScreen", 0, failSafeTime)) { + break + } else { + adbClick_wbb(146, 494) ;146, 494 + Delay(1) + } + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting for ConfirmPack`n(" . failSafeTime . "/45 seconds)") + if(failSafeTime > 45) + restartGameInstance("Stuck at ConfirmPack") + } + + CardDetection_FlushPendingGodPack() +} + +ReceiveGiftExtended() { + global session, receivedGiftOnly + + if (HasFlagInMetadata(session.get("accountFileName"), "R")) + return false + + ; Reach gift screen with a timeout to avoid endless loops on UI desync. + foundClaimable := false + foundClaimedAll := false + minClaimableWaitSeconds := 5 + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + if(FindOrLoseImage("Common_ShopButtonInMain", 0)) { + adbClick_wbb(247, 93) + Delay(4) + } + + if(FindOrLoseImage("Gift_Claimable", 0, 0, 25)) { + foundClaimable := true + break + } + + elapsedSeconds := (A_TickCount - session.get("failSafe")) // 1000 + if(elapsedSeconds >= minClaimableWaitSeconds && FindOrLoseImage("Gift_ClaimedAll", 0, 0, 25)) { + foundClaimedAll := true + break + } + + failSafeTime := elapsedSeconds + if(failSafeTime > 45) + break + } + failSafeTime := 0 + if(!foundClaimable) { + if(foundClaimedAll || (elapsedSeconds >= minClaimableWaitSeconds && FindOrLoseImage("Gift_ClaimedAll", 0, 0, 25))) + return false + return false + } + + maxClaimAttempts := 12 + claimConfirmed := false + Loop, %maxClaimAttempts% { + adbClick(212, 427) + Delay(3) + if (FindOrLoseImage("Gift_ReceivedWindowRightBorder", 0, 0, 25)) { + claimConfirmed := true + adbInputEvent("111") + Delay(2) + break + } + if(FindOrLoseImage("Pack_ReadyForOpenPack", 1, 0)){ + claimConfirmed := true + adbInputEvent("111") + Delay(2) + break + } + } + + if(!claimConfirmed) + return false + + Delay(1) + + receivedGiftOnly := true + return true +} + +HandleGiftedPacksAfterReceiveGift() { + global session + + handledGiftPacks := 0 + maxGiftPacks := 20 + sawReceivedWindow := false + rewardSetHandled := false + + session.set("failSafe", A_TickCount) + failSafeTime := 0 + + Loop { + if (rewardSetHandled || !FindOrLoseImage("Gift_Claimable", 0, 1)) { + if(FindOrLoseImage("Gift_ClaimedAll", 0, 1)) { + adbInputEvent("111") + Delay(1) + break + } + } + + if(FindOrLoseImage("Gift_ReceivedWindowRightBorder", 0, 1)) { + adbInputEvent("111") + Delay(1) + sawReceivedWindow := true + rewardSetHandled := true + session.set("failSafe", A_TickCount) + failSafeTime := 0 + continue + } + + if(FindOrLoseImage("Pack_ReadyForOpenPack", 0, 1)) { + HandleSingleGiftPackOpening() + handledGiftPacks++ + rewardSetHandled := true + + if(handledGiftPacks >= maxGiftPacks) + break + + session.set("failSafe", A_TickCount) + failSafeTime := 0 + continue + } + + if(FindOrLoseImage("Pack_SkipButtonAfterOpenPack", 0, 1)) { + adbClick_wbb(247, 500) + } else if(FindOrLoseImage("Pack_NextButtonAfterOpenPack", 0, 1) || FindOrLoseImage("Next2", 0, 1)) { + adbClick_wbb(146, 489) + } else if(FindOrLoseImage("Pack_ResultAfterOpenPack", 0, 1)) { + adbClick_wbb(247, 500) + } else { + adbClick_wbb(146, 489) + } + + Delay(1) + + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage((sawReceivedWindow ? "Finalizing gifts" : "Handling gifted packs") . "`n(" . failSafeTime . "/90 seconds)") + if(failSafeTime > 90) + break + } +} + +SetOpenGiftSpeed(targetSpeed) { + if(targetSpeed != 1 && targetSpeed != 2 && targetSpeed != 3) + return false + + SetOpenGiftSpeedByButtons(targetSpeed) + + return true +} + +SetOpenGiftSpeedByButtons(targetSpeed) { + FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) + if(targetSpeed = 1) + FindImageAndClick(GetSpeedModNeedle(1), GetSpeedModClickX(1), GetSpeedModClickY(1)) + else if(targetSpeed = 3) + FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) + else + FindImageAndClick(GetSpeedModNeedle(2), GetSpeedModClickX(2), GetSpeedModClickY(2)) + + Delay(1) + adbClick_wbb(51, 297) + Delay(1) + return true +} + +HandleSingleGiftPackOpening() { + global session, adbSwipeParams + + session.set("openedGiftPack", true) + + if(session.get("setSpeed") > 1) { + SetOpenGiftSpeed(1) + } + + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + adbSwipe_wbb(adbSwipeParams) + Sleep, 100 + ; Confirm swipe by requiring both ready markers to stay gone for a short window. + if(FindOrLoseImage("Pack_ReadyForOpenPack", 1, failSafeTime)) { + swipeConfirmed := true + Loop, 4 { + Sleep, 150 + if(FindOrLoseImage("Pack_ReadyForOpenPack", 0, 0, 20, 1)) { + swipeConfirmed := false + break + } + } + + if(swipeConfirmed) { + if(session.get("setSpeed") > 1) { + SetOpenGiftSpeed(session.get("setSpeed")) + } + break + } + } + + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Opening gifted pack`n(" . failSafeTime . "/45 seconds)") + if(failSafeTime > 45) { + if(session.get("setSpeed") > 1) { + SetOpenGiftSpeed(session.get("setSpeed")) + } + restartGameInstance("Stuck at gifted pack swipe") + return + } + } + + FindImageAndClick("Gift_ResultAfterOpenPack", 252, 505, 5, 25) + + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + if(FindOrLoseImage("Gift_ReceivedWindowRightBorder", 0, 1)) { + break + } else if(FindOrLoseImage("Pack_ReadyForOpenPack", 0, 1)) { + break + } else if(FindOrLoseImage("Create_SwipeForRegisterDexIcon", 0, 1)) { + break + } else if(FindOrLoseImage("Pack_SkipButtonAfterOpenPack", 0, 1)) { + adbClick_wbb(247, 500) + } else if(FindOrLoseImage("Pack_NextButtonAfterOpenPack", 0, 1) || FindOrLoseImage("Next2", 0, 1)) { + adbClick_wbb(146, 489) + } else { + adbClick_wbb(146, 489) + } + + Delay(1) + + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Advancing gifted pack`n(" . failSafeTime . "/45 seconds)") + if(failSafeTime > 45) + break + } + + writeLastActivityEpoch(session.get("scriptName")) +} diff --git a/Scripts/Include/BotStopSignals.ahk b/Scripts/Include/BotStopSignals.ahk new file mode 100644 index 00000000..c79db5c8 --- /dev/null +++ b/Scripts/Include/BotStopSignals.ahk @@ -0,0 +1,74 @@ +; Stop/exit signalling between instances and cleanup on exit + +PersistStopAfterRunIfNeeded() { + global session + if (session.get("stopToggle")) + IniWrite, 1, % session.get("scriptIniFile"), UserSettings, stopAfterRunPending +} + +; Kill all script instances immediately +StopAllInstances() { + global botConfig + + DetectHiddenWindows, On + SetTitleMatchMode, 2 ; Match if title CONTAINS the string (needed for full paths) + + ; Close Main.ahk first + WinClose, Main.ahk ahk_class AutoHotkey + + ; Close all numbered instances (2 through Instances, skip 1 which is us) + Loop, % botConfig.get("Instances") { + if (A_Index != 1) { + WinClose, % A_Index ".ahk ahk_class AutoHotkey" + } + } + + ; Finally exit this instance + CleanupBeforeExit() + ExitApp +} + +OnMonitorWake(wParam, lParam, msg, hwnd) { + global session + + DelayH(3000) + FixInstanceScreen(session.get("scriptName")) +} + +; Message handler for "stop after run" signal from instance 1 +OnStopAfterRunMessage(wParam, lParam, msg, hwnd) { + global session + session.set("stopToggle", true) + CreateStatusMessage("Stopping script at the end of the run...",,,, false) + return 0 +} + +; Send "stop after run" message to all other script instances +SignalStopAfterRun() { + global botConfig + + DetectHiddenWindows, On + SetTitleMatchMode, 2 + + ; Send message to all numbered instances (2 through Instances) + Loop, % botConfig.get("Instances") { + if (A_Index != 1) { + ; Find the window for this instance + WinGet, targetHwnd, ID, % A_Index ".ahk ahk_class AutoHotkey" + if (targetHwnd) { + ; Send custom message (0x500) to signal "stop after run" + PostMessage, 0x500, 0, 0,, ahk_id %targetHwnd% + } + } + } +} + +CleanupBeforeExit(){ + global session + + TerminateHelper() + CloseCardDatabase(session.get("deviceAccount")) + AccountMetadata_CloseTempForInstance(session.get("scriptName")) + allSpecialEventDispose() + GetGPUMemoryByPDH(-1, true) +} diff --git a/Scripts/Include/BotToggles.ahk b/Scripts/Include/BotToggles.ahk new file mode 100644 index 00000000..b5960a51 --- /dev/null +++ b/Scripts/Include/BotToggles.ahk @@ -0,0 +1,144 @@ +; Toggle handlers for stop, GP test, dev mode and bounding-box debugging + +; ToggleStop - For GUI button clicks (stops only THIS instance) +ToggleStop() { + global botConfig, session, dictionaryData + + ; Check if user has a saved preference for single instance stop + botConfig.loadIniSectionFromSettingsFile("Extra") + savedStopPreferenceSingle := (botConfig.get("stopPreferenceSingle") = "") ? "none" : botConfig.get("stopPreferenceSingle") + + if (savedStopPreferenceSingle != "none" && savedStopPreferenceSingle != "ERROR" && savedStopPreferenceSingle != "") { + ; Execute the saved preference directly without showing popup + if (savedStopPreferenceSingle = "immediate") { + CleanupBeforeExit() + ExitApp + } else if (savedStopPreferenceSingle = "wait_end") { + session.set("stopToggle", true) + CreateStatusMessage("Stopping script at the end of the run...",,,, false) + } + return + } + + ; Get localized strings + title := dictionaryData[botConfig.get("defaultBotLanguage")]["stop_confirm_title"] + btnImmediate := dictionaryData[botConfig.get("defaultBotLanguage")]["stop_immediately"] + btnWaitEnd := dictionaryData[botConfig.get("defaultBotLanguage")]["stop_wait_end"] + chkRemember := dictionaryData[botConfig.get("defaultBotLanguage")]["stop_remember_preference"] + + ; Create confirmation GUI with checkbox + Gui, StopConfirm:New, +AlwaysOnTop +Owner + Gui, StopConfirm:Add, Text, x20 y15 w260 Center, % title + Gui, StopConfirm:Add, Button, x20 y45 w130 h30 gStopImmediatelySingle, % btnImmediate + Gui, StopConfirm:Add, Button, x160 y45 w130 h30 gStopWaitEndSingle, % btnWaitEnd + Gui, StopConfirm:Add, Checkbox, x20 y85 w260 hwndhRememberStopPreferenceSingle, % chkRemember + Gui, StopConfirm:Show, w310 h115, % title + + session.set("RememberStopPreferenceSingleHwnd", hRememberStopPreferenceSingle) + return +} + +; ToggleStopAll - For Shift+F7 hotkey (stops ALL instances, only called from instance 1) +ToggleStopAll() { + static ui_RememberStopPreference + global botConfig, session, dictionaryData + + ; Check if user has a saved preference + botConfig.loadIniSectionFromSettingsFile("Extra") + savedStopPreference := (botConfig.get("stopPreference") = "") ? "none" : botConfig.get("stopPreference") + + if (savedStopPreference != "none" && savedStopPreference != "ERROR" && savedStopPreference != "") { + ; Execute the saved preference directly without showing popup + if (savedStopPreference = "immediate") { + StopAllInstances() + } else if (savedStopPreference = "wait_end") { + SignalStopAfterRun() + session.set("stopToggle", true) + CreateStatusMessage("Stopping script at the end of the run...",,,, false) + } else if (savedStopPreference = "kill_mumu") { + Loop, % botConfig.get("Instances") { + killInstance(A_Index) + Sleep, 500 + } + Sleep, 1000 + StopAllInstances() + } + return + } + + ; Get localized strings + title := dictionaryData[botConfig.get("defaultBotLanguage")]["stop_confirm_title"] + btnImmediate := dictionaryData[botConfig.get("defaultBotLanguage")]["stop_immediately"] + btnWaitEnd := dictionaryData[botConfig.get("defaultBotLanguage")]["stop_wait_end"] + btnKillMumu := dictionaryData[botConfig.get("defaultBotLanguage")]["stop_kill_mumu"] + chkRemember := dictionaryData[botConfig.get("defaultBotLanguage")]["stop_remember_preference"] + + ; Create confirmation GUI with checkbox + Gui, StopConfirmAll:New, +AlwaysOnTop +Owner + Gui, StopConfirmAll:Add, Text, x20 y15 w400 Center, % title + Gui, StopConfirmAll:Add, Button, x20 y45 w130 h30 gStopImmediatelyAll, % btnImmediate + Gui, StopConfirmAll:Add, Button, x160 y45 w130 h30 gStopWaitEndAll, % btnWaitEnd + Gui, StopConfirmAll:Add, Button, x300 y45 w130 h30 gStopAndKillMuMuAll, % btnKillMumu + Gui, StopConfirmAll:Add, Checkbox, x20 y85 w400 hwndhRememberStopPreference, % chkRemember + Gui, StopConfirmAll:Show, w450 h115, % title + + session.set("RememberStopPreferenceHwnd", hRememberStopPreference) + return +} + +ToggleTestScript() { + global session + + if(!session.get("GPTest")) { + CreateStatusMessage("In GP Test Mode",,,, false) + session.set("GPTest", true) + } + else { + CreateStatusMessage("Exiting GP Test Mode",,,, false) + session.set("GPTest", false) + } +} + +ToggleDevMode() { + global session + + try { + OwnerWND := getMuMuHwnd(session.get("winTitle")) + if (!OwnerWND) { + CreateStatusMessage("Failed to create button GUI. Emulator window not found.",,,, false) + return + } + + WinGetPos, x, y, Width, Height, ahk_id %OwnerWND% + x4 := x + 5 + y4 := y + 44 + buttonWidth := 40 + + guiSuffix := session.get("winTitle") + Gui, DevMode%guiID%:Destroy + Gui, DevMode%guiID%:New, +Owner%OwnerWND% +LastFound + Gui, DevMode%guiID%:Font, s5 cGray Norm Bold, Segoe UI ; Normal font for input labels + Gui, DevMode%guiID%:Add, Button, % "x" . (buttonWidth * 0) . " y0 w" . buttonWidth . " h25 gbboxScript", bound box + + Gui, DevMode%guiID%:Add, Button, % "x" . (buttonWidth * 1) . " y0 w" . buttonWidth . " h25 gbboxNpauseScript", bbox pause + + Gui, DevMode%guiID%:Add, Button, % "x" . (buttonWidth * 2) . " y0 w" . buttonWidth . " h25 gscreenshotscript", screen grab + ;Gui, DevMode%guiID%:Add, Button, % "x" . (buttonWidth * 3) . " y0 w" . buttonWidth . " h25 gStartStopRecording", Start Recording + + Gui, DevMode%guiID%:Add, Button, % "x" . (buttonWidth * 0) . " y" . (25 + 5) " w" . buttonWidth . " h25 gLogout", Logout + + Gui, DevMode%guiID%:Show, x%x4% y%y4% w250 h100, % "Dev Mode" session.get("winTitle") + + } + catch e { + CreateStatusMessage("Failed to create button GUI. " . e.Message,,,, false) + } +} + +ToggleBBox() { + session.set("dbg_bbox", !session.get("dbg_bbox")) +} + +TogglebboxNpause() { + session.set("dbg_bboxNpause", !session.get("dbg_bboxNpause")) +} diff --git a/Scripts/Include/Crinity_UnofficialPatch.ahk b/Scripts/Include/Crinity_UnofficialPatch.ahk index a2e4a3e6..0ca05c97 100644 --- a/Scripts/Include/Crinity_UnofficialPatch.ahk +++ b/Scripts/Include/Crinity_UnofficialPatch.ahk @@ -60,6 +60,7 @@ TryDismissPackOpeningRecoveryFinding(failSafeTime := 0) { ; so mode clicks (e.g. Social tab) can run. Do NOT call from boot. ; Returns true when a dismiss click was issued this tick. TryAdvanceWelcomeBackWhileFinding(logContext, ByRef advancing, ByRef useSecondClick, failSafeTime := 0) { + ; todo: handle pack clicks ; Pack recovery can land right after the two Welcome Back pages; stop sticky so Finding can dismiss it. if (FindOrLoseImage("Common_AlertForAppCrachDuringOpenPack", 0, 0, , true)) { if (advancing) { From 600ef9a6735731179e7946e2cf116c7c8f82a98d Mon Sep 17 00:00:00 2001 From: Lean Date: Sun, 4 Oct 2026 15:18:39 +0200 Subject: [PATCH 05/10] Extract more functions --- Scripts/1.ahk | 1708 +--------------------------- Scripts/Include/BotAccountMenu.ahk | 149 +++ Scripts/Include/BotDebugTools.ahk | 285 +++++ Scripts/Include/BotMissions.ahk | 524 +++++++++ Scripts/Include/BotTutorial.ahk | 560 +++++++++ Scripts/Include/BotWonderPick.ahk | 190 ++++ 6 files changed, 1713 insertions(+), 1703 deletions(-) create mode 100644 Scripts/Include/BotAccountMenu.ahk create mode 100644 Scripts/Include/BotDebugTools.ahk create mode 100644 Scripts/Include/BotMissions.ahk create mode 100644 Scripts/Include/BotTutorial.ahk create mode 100644 Scripts/Include/BotWonderPick.ahk diff --git a/Scripts/1.ahk b/Scripts/1.ahk index 7a332396..745bb59c 100644 --- a/Scripts/1.ahk +++ b/Scripts/1.ahk @@ -20,6 +20,11 @@ pToken := Gdip_Startup() #Include BotStopSignals.ahk #Include BotPackEvaluation.ahk #Include BotPackOpening.ahk +#Include BotMissions.ahk +#Include BotWonderPick.ahk +#Include BotTutorial.ahk +#Include BotAccountMenu.ahk +#Include BotDebugTools.ahk InitializeHiddenConsole() @@ -885,93 +890,6 @@ if(DeadCheck = 1 && botConfig.get("deleteMethod") != "Create Bots (13P)") { return -HomeAndMission(homeonly := 0, completeSecondMisson=false) { - global session - - Sleep, 250 - Leveled := 0 - Loop { - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - FindImageAndClick("Mission_ThemeCollectionButtonIcon", 261, 478, , 1000, 1) - Delay(1) - - if(FindOrLoseImage("Mission_ThemeCollectionButtonIcon", 0, failSafeTime)){ - break - } - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - } - if(!homeonly){ - FindImageAndClick("Mission_ThemeCollectionButtonIcon", 261, 478, , 1000) - - wonderpicked := 0 - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - Delay(1) - - Loop { - if (completeSecondMisson){ - adbClick_wbb(150, 390) - } - else { - adbClick_wbb(150, 286) - } - Delay(1) - - if(FindOrLoseImage("Mission_ThemeCollectionButtonIcon", 1, , 10)){ - break - } - } - - if(FindOrLoseImage("Mission_MissionIconTopAreaInDetails", 0, failSafeTime) || FindOrLoseImage("Mission_MissionIconTopAreaInDetailsAlt", 0, failSafeTime)) - break - - if(FindOrLoseImage("Create_SoloBattleMissionIconInDetail", 0, failSafeTime)) { - SetBeginnerMissionsDone() - return - } - - if (FindOrLoseImage("Mission_FirstWonderpickMissionIconInDetails", 0, failSafeTime)){ - adbClick_wbb(141, 396) ; click try it and go to wonderpick page - DoWonderPickOnly() - wonderpicked := 1 - break - } - - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - } - if(!wonderpicked) - break - } else - break - } - - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - Delay(1) - - adbClick_wbb(139, 424) ;clicks complete mission - Delay(1) - clickButton := FindOrLoseImage("Common_ColorChangeButton", 0, failSafeTime, 80) - if(clickButton) { - adbClick_wbb(110, 435) - Delay(1) - } - else if(FindOrLoseImage("Common_ShopButtonInMain", 1, failSafeTime)) { - GoToMain() - break - } - else - break - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("In failsafe for WonderPick. " . failSafeTime "/45 seconds") - } - return Leveled -} - FindOrLoseImage(needleName := "DEFAULT", EL := 1, safeTime := 0, searchVariation := 20, notShowFinding := 0, coordImageName := "", coordEL := "", coordSafeTime := "") { prof := Prof_Scope(A_ThisFunc) profNeedle := Prof_Scope(A_ThisFunc . ":" . needleName) @@ -1317,24 +1235,6 @@ DirectlyPositionWindow() { return true } -FinalizeInjectedGodPackAccount(testing := True) { - global botConfig, session, DeadCheck - - if (!session.get("injectMethod") || !session.get("loadedAccount")) - return - - if(testing) { - session.get("missionDoneList")["accountHasPackInTesting"] := 1 - } - setMetaData() - IniWrite, 0, % session.get("scriptIniFile"), UserSettings, DeadCheck - - if (botConfig.get("deleteMethod") = "Inject 13P+" || botConfig.get("deleteMethod") = "Inject Wonderpick 96P+") { - MarkAccountAsUsed() - session.set("loadedAccount", false) - } -} - restartGameInstance(reason, RL := true) { prof := Prof_Scope(A_ThisFunc) global botConfig, session, DeadCheck @@ -1424,346 +1324,11 @@ restartGameInstance(reason, RL := true) { } } -SaveStuckScreenshot(reason) { - prof := Prof_Scope(A_ThisFunc) - global session - - fileDir := A_ScriptDir . "\..\Screenshots\Stuck" - if !FileExist(fileDir) - FileCreateDir, %fileDir% - - safeReason := RegExReplace(reason, "[\\/:*?""<>|]", "_") - safeReason := RegExReplace(safeReason, "\s+", "_") - safeReason := SubStr(safeReason, 1, 80) - - deviceAccount := GetCurrentDeviceAccountForMetadata() - if (deviceAccount = "") - deviceAccount := "unknown_device_account" - safeDeviceAccount := RegExReplace(deviceAccount, "[\\/:*?""<>|]", "_") - safeDeviceAccount := RegExReplace(safeDeviceAccount, "\s+", "_") - safeDeviceAccount := SubStr(safeDeviceAccount, 1, 80) - - filePath := fileDir . "\" . A_Now . "_inst" . session.get("scriptName") . "_" . safeDeviceAccount . "_" . safeReason . ".png" - - hWnd := getMuMuHwnd(session.get("winTitle")) - if (!hWnd) - return - - pBitmap := from_window(hWnd) - if (pBitmap) { - Gdip_SaveBitmapToFile(pBitmap, filePath) - Gdip_DisposeImage(pBitmap) - LogInfo("Saved stuck screenshot: " . filePath) - } -} - -menuDelete() { - prof := Prof_Scope(A_ThisFunc) - global botConfig, session - - Delay(1) - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop - { - Delay(2) - adbClick_wbb(245, 518) - if(FindImageAndClick("Menu_InventoryIconInMenu", , , , , 1, failSafeTime)) ;wait for settings menu - break - Delay(2) - adbClick_wbb(50, 100) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for Settings`n(" . failSafeTime . " seconds)") - } - Delay(1) - FindImageAndClick("Menu_SettingButtonInMenu", 140, 440, , 2000) ;wait for other menu - Delay(1) - FindImageAndClick("Menu_RemoveAccountNintendoButtonInMenu", 79, 256, , 1000) ;wait for account menu - Delay(1) - - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - clickButton := FindOrLoseImage("Common_UnknownButton2", 0, failSafeTime, 40) - if(!clickButton) { - ; fix https://discord.com/channels/1330305075393986703/1354775917288882267/1355090394307887135 - clickImage := FindOrLoseImage("Menu_DeleteConfimButtonStep1", 0, failSafeTime, 60) - if(clickImage) { - StringSplit, pos, clickImage, `, ; Split at ", " - adbClick_wbb(pos1, pos2) - } - else { - adbClick_wbb(198, 480) - Delay(2) - adbClick_wbb(230, 506) - } - Delay(1) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting to click delete`n(" . failSafeTime . "/45 seconds)") - } - else { - break - } - Delay(1) - } - StringSplit, pos, clickButton, `, ; Split at ", " - adbClick_wbb(pos1, pos2) - break - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting to click delete`n(" . failSafeTime . "/45 seconds)") - } - - Sleep, 2500 -} - -menuDeleteStart() { - prof := Prof_Scope(A_ThisFunc) - global botConfig, session - - if(session.get("keepAccount")) { - return session.get("keepAccount") - } - if(session.get("friended")) { - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - if(session.get("setSpeed") = 3) - FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) - else - FindImageAndClick(GetSpeedModNeedle(2), GetSpeedModClickX(2), GetSpeedModClickY(2)) - Delay(1) - adbClick_wbb(51, 297) - Delay(1) - } - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - if(!session.get("friended")) - break - adbClick_wbb(255, 83) - if(FindOrLoseImage("Create_CountryComboBoxButton", 0, failSafeTime)) { ;if at country continue - break - } - else if(FindOrLoseImage("Menu_AgreementIconInIntroMenu", 0, failSafeTime)) { ; if the clicks in the top right open up the game settings menu then continue to delete account - Delay(1) - FindImageAndClick("Menu_RemoveAccountNintendoButtonInMenu", 79, 256, , 1000) ;wait for account menu - Delay(1) - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - clickButton := FindOrLoseImage("Common_ColorChangeButton", 0, failSafeTime, 80) - if(!clickButton) { - clickImage := FindOrLoseImage("Menu_DeleteConfimButtonStep1", 0, failSafeTime, 60) - if(clickImage) { - StringSplit, pos, clickImage, `, ; Split at ", " - adbClick_wbb(pos1, pos2) - } - else { - adbClick_wbb(230, 506) - } - Delay(1) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting to click delete`n(" . failSafeTime . "/45 seconds)") - } - else { - break - } - Delay(1) - } - StringSplit, pos, clickButton, `, ; Split at ", " - adbClick_wbb(pos1, pos2) - break - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting to click delete`n(" . failSafeTime . "/45 seconds)") - } - CreateStatusMessage("Looking for Country/Menu") - Delay(1) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for Country/Menu`n(" . failSafeTime . "/45 seconds)") - } - if(session.get("loadedAccount")) { - ; FileDelete, % session.get("loadedAccount") - } -} - ControlClick(X, Y) { global session ControlClick, x%X% y%Y%, % session.get("winTitle") } -Screenshot_dev(fileType := "Dev", subDir := "", srcPath := "") { - prof := Prof_Scope(A_ThisFunc) - global session, rec_Active - SetWorkingDir %A_ScriptDir% ; Ensures the working directory is the script's directory - - ; Define folder and file paths - fileDir := A_ScriptDir "\..\Screenshots\grab" - if !FileExist(fileDir) - FileCreateDir, %fileDir% - if (subDir) { - fileDir .= "\" . subDir - } - if !FileExist(fileDir) - FileCreateDir, %fileDir% - - ; File path for saving the screenshot locally - fileName := A_Now . "_" . session.get("scriptName") . "_" . fileType . ".png" - filePath := fileDir "\" . fileName - - if (srcPath != "" && FileExist(srcPath)) - pBitmapW := Gdip_CreateBitmapFromFile(srcPath) - else - pBitmapW := from_window(getMuMuHwnd(session.get("winTitle"))) - Gdip_SaveBitmapToFile(pBitmapW, filePath) - - sleep 100 - - try { - CoordMode, Mouse, Client - OwnerWND := WinExist(session.get("winTitle")) - buttonWidth := 40 - - guiSuffix := session.get("winTitle") - Gui, DevMode_ss%guiSuffix%:New, +LastFound -DPIScale - Gui, DevMode_ss%guiSuffix%:Add, Picture, x0 y0 w275 h528 hwndhAppScreen, %filePath% - Gui, DevMode_ss%guiSuffix%:Show, w275 h528, % "Screensho" session.get("winTitle") - - GuiControlGet, PicPos, Pos, %hAppScreen% - - sleep 100 - msgbox click on top-left corner and bottom-right corners - - yBias := 40 - - KeyWait, LButton, D - MouseGetPos , X1, Y1, OutputVarWin, OutputVarControl - KeyWait, LButton, U - X1 := X1 - PicPosX - Y1 := Y1 - PicPosY - - ; Return in case of user close the screen - if !WinExist("Screensho" session.get("winTitle")) - return - - KeyWait, LButton, D - MouseGetPos , X2, Y2, OutputVarWin, OutputVarControl - KeyWait, LButton, U - X2 := X2 - PicPosX - Y2 := Y2 - PicPosY - - W:=X2-X1 - H:=Y2-Y1 - - MsgBox, % X1 ", " Y1 " / " X2 ", " Y2 - - pBitmap := Gdip_CloneBitmapArea(pBitmapW, X1, Y1, W, H) - - InputBox, fileName, ,"Enter the name of the needle to save" - - if (fileName = "") { - return "" - } - - fullScreenPath := filePath - fileDir := A_ScriptDir . "\Needles" - filePath := fileDir "\" . fileName . ".png" - Gdip_SaveBitmapToFile(pBitmap, filePath) - - KeyWait, LButton, D - MouseGetPos , X3, Y3, OutputVarWin, OutputVarControl - KeyWait, LButton, U - X3 := X3 - PicPosX - Y3 := Y3 - PicPosY - - global rec_LastScreenGrab - rec_LastScreenGrab := {fileName: fileName, needlePath: filePath - , screenshot: fullScreenPath - , x1: X1, y1: Y1, x2: X2, y2: Y2, x3: X3, y3: Y3} - - ; Convert window coordinates to device/OCR coordinates - ; Device resolution: 540x960, Window resolution: 277x489, Y offset: 44 - OCR_X1 := Round(X1 * 540 / 283) - OCR_Y1 := Round((Y1 - 44) * 960 / 488) - OCR_W := Round(W * 540 / 283) - OCR_H := Round(H * 960 / 488) - OCR_X2 := OCR_X1 + OCR_W - OCR_Y2 := OCR_Y1 + OCR_H - - ; Calculate center point of the box - OCR_X3 := Round(OCR_X1 + OCR_W / 2) - OCR_Y3 := Round(OCR_Y1 + OCR_H / 2) - - MsgBox, - (LTrim - ctrl+C to copy: - FindOrLoseImage(%X1%, %Y1%, %X2%, %Y2%, , "%fileName%", 0, failSafeTime) - FindImageAndClick(%X1%, %Y1%, %X2%, %Y2%, , "%fileName%", %X3%, %Y3%, sleepTime) - adbClick_wbb(%X3%, %Y3%) - OCR coordinates: %OCR_X3%, %OCR_Y3%, %OCR_W%, %OCR_H% - ) -} -catch { - msgbox Failed to create screenshot GUI -} -CoordMode, Pixel, Screen -return filePath -} - -Screenshot(fileType := "Valid", subDir := "", ByRef fileName := "") { - prof := Prof_Scope(A_ThisFunc) - SetWorkingDir %A_ScriptDir% ; Ensures the working directory is the script's directory - - ; Define folder and file paths - fileDir := A_ScriptDir "\..\Screenshots" - if !FileExist(fileDir) - FileCreateDir, %fileDir% - if (subDir) { - fileDir .= "\" . subDir - if !FileExist(fileDir) - FileCreateDir, %fileDir% - } - if (filename = "PACKSTATS") { - fileDir .= "\temp" - if !FileExist(fileDir) - FileCreateDir, %fileDir% - } - - ; File path for saving the screenshot locally - fileName := A_Now . "_" . session.get("scriptName") . "_" . fileType . "_" . session.get("packsInPool") . "_packs.png" - if (filename = "PACKSTATS") - fileName := "packstats_temp.png" - filePath := fileDir "\" . fileName - - yBias := 40 - cropX := 18 - cropY := 170 - cropW := 240 - cropH := 227 - - if (fileType = "FRIENDCODE"){ - cropX := 18 - cropY := 66 - cropW := 240 - cropH := 165 - } - - pBitmapW := from_window(getMuMuHwnd(session.get("winTitle"))) - pBitmap := Gdip_CloneBitmapArea(pBitmapW, cropX, cropY, cropW, cropH) - - Gdip_DisposeImage(pBitmapW) - Gdip_SaveBitmapToFile(pBitmap, filePath) - - ; Don't dispose pBitmap if it's a PACKSTATS screenshot - if (filename != "PACKSTATS") { - Gdip_DisposeImage(pBitmap) - return filePath - } - - ; For PACKSTATS, return both values and delete temp file after OCR is done - return {filepath: filePath, bitmap: pBitmap, deleteAfterUse: true} -} - ; Pause Script PauseScript: CreateStatusMessage("Pausing...",,,, false) @@ -2026,85 +1591,6 @@ bboxNpauseScript: TogglebboxNpause() return -bboxDraw2(X1, Y1, X2, Y2, color) { - global session - - WinGetPos, xwin, ywin, Width, Height, % session.get("winTitle") - BoxWidth := 10 - BoxHeight := 10 - Xm1:=X1-(BoxWidth/2) - Xm2:=X2-(BoxWidth/2) - Ym1:=Y1-(BoxWidth/2) - Ym2:=Y2-(BoxWidth/2) - Xh1:=Xm1+BoxWidth - Xh2:=Xm2+BoxWidth - Yh1:=Ym1+BoxHeight - Yh2:=Ym2+BoxHeight - - ; Create a GUI - guiSuffix := session.get("winTitle") - Gui, BoundingBox%guiSuffix%:+AlwaysOnTop +ToolWindow -Caption +E0x20 - Gui, BoundingBox%guiSuffix%:Color, 123456 - Gui, BoundingBox%guiSuffix%:+LastFound ; Make the GUI window the last found window for use by the line below. (straght from documentation) - WinSet, TransColor, 123456 ; Makes that specific color transparent in the gui - - ; Create the borders and show - Gui, BoundingBox%guiSuffix%:Add, Progress, x%Xm1% y%Ym1% w%BoxWidth% h2 %color% - Gui, BoundingBox%guiSuffix%:Add, Progress, x%Xm1% y%Ym1% w2 h%BoxHeight% %color% - Gui, BoundingBox%guiSuffix%:Add, Progress, x%Xh1% y%Ym1% w2 h%BoxHeight% %color% - Gui, BoundingBox%guiSuffix%:Add, Progress, x%Xm1% y%Yh1% w%BoxWidth% h2 %color% - - ; Create the borders and show - Gui, BoundingBox%guiSuffix%:Add, Progress, x%Xm2% y%Ym2% w%BoxWidth% h2 %color% - Gui, BoundingBox%guiSuffix%:Add, Progress, x%Xm2% y%Ym2% w2 h%BoxHeight% %color% - Gui, BoundingBox%guiSuffix%:Add, Progress, x%Xh2% y%Ym2% w2 h%BoxHeight% %color% - Gui, BoundingBox%guiSuffix%:Add, Progress, x%Xm2% y%Yh2% w%BoxWidth% h2 %color% - - xshow := xwin - yshow := ywin - Gui, BoundingBox%guiSuffix%:Show, x%xshow% y%yshow% NoActivate - Sleep, 100 - -} - -adbSwipe_wbb(params) { - prof := Prof_Scope(A_ThisFunc) - global session - - if(session.get("dbg_bbox")) - bboxAndPause_swipe(params, session.get("dbg_bboxNpause")) - adbSwipe(params) -} - -bboxAndPause_swipe(params, doPause := False) { - global session - - guiSuffix := session.get("winTitle") - paramsplit := StrSplit(params , " ") - X1:=round(paramsplit[1] / 535 * 283) - Y1:=round((paramsplit[2] / 960 * 488) + 40) - X2:=round(paramsplit[3] / 535 * 283) - Y2:=round((paramsplit[4] / 960 * 488) + 40) - speed:=paramsplit[5] - CreateStatusMessage("Swiping (" . X1 . "," . Y1 . ") to (" . X2 . "," . Y2 . ") speed " . speed,,,, false) - - color := "BackgroundYellow" - - ;bboxDraw2(X1, Y1, X2, Y2, color) - - bboxDraw(X1-5, Y1-5, X1+5, Y1+5, color) - if (doPause) { - Pause - } - Gui, BoundingBox%guiSuffix%:Destroy - - bboxDraw(X2-5, Y2-5, X2+5, Y2+5, color) - if (doPause) { - Pause - } - Gui, BoundingBox%guiSuffix%:Destroy -} - Gdip_ImageSearch_wbb(pBitmapHaystack,pNeedle,ByRef OutputList="" ,OuterX1=0,OuterY1=0,OuterX2=0,OuterY2=0,Variation=0,Trans="" ,SearchDirection=1,Instances=1,LineDelim="`n",CoordDelim=",") { @@ -2138,1190 +1624,6 @@ GetNeedle(Path) { } } -DoTutorial() { - prof := Prof_Scope(A_ThisFunc) - global botConfig, session - - session.set("creationDate", A_Now) - - FindImageAndClick("Create_CountryComboBoxButton", 143, 370) ;select month and year and click - - Delay(3) - adbClick_wbb(80, 400) - Delay(3) - - tutorialLanguage := Trim(session.get("language")) - if (tutorialLanguage = "") { - ; New accounts have no metadata yet, so read the active game's language - if (EnsurePTCGPBHelperInstalled()) { - tutorialLanguage := adbWriteRaw("/data/ptcgp/ptcgpb lang", true) - tutorialLanguage := Trim(StrReplace(tutorialLanguage, "`r"), "`n`t ") - if (RegExMatch(tutorialLanguage, "i)^[a-z][a-z0-9_-]{0,15}$")) - session.set("language", tutorialLanguage) - else - tutorialLanguage := "" - } - } - StringLower, tutorialLanguage, tutorialLanguage - isYearInFront := (tutorialLanguage = "ja" || tutorialLanguage = "ko" || tutorialLanguage = "zh") - if (isYearInFront) { - ; JP/KR display the month picker in the opposite order. - Random, monthScrollCount, 4, 7 - Loop, %monthScrollCount% { - adbSwipe_wbb("100 707 100 903 150") - Delay(3) - } - } - - adbClick_wbb(80, 375) - Delay(3) - session.set("failSafe", A_TickCount) - failSafeTime := 0 - - Loop { - Delay(3) - if(FindImageAndClick("Create_SelectedYear", , , , , 1, failSafeTime)) - break - Delay(3) - adbClick_wbb(142, 159) - Delay(3) - adbClick_wbb(80, 400) - Delay(3) - adbClick_wbb(80, 375) - Delay(3) - adbClick_wbb(82, 422) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for Year`n(" . failSafeTime . "/45 seconds)") - } ;select month and year and click - - adbClick_wbb(200, 400) - Delay(3) - - if (!isYearInFront) { - ; Other languages display the year picker in the opposite order. - Random, yearScrollCount, 4, 7 - Loop, %yearScrollCount% { - adbSwipe_wbb("389 707 389 903 150") - Delay(3) - } - } - adbClick_wbb(200, 375) - Delay(3) - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { ;select month and year and click - Delay(3) - if(FindImageAndClick("Create_SelectedMonth", , , , , 1, failSafeTime)) - break - Delay(3) - adbClick_wbb(142, 159) - Delay(3) - adbClick_wbb(142, 159) - Delay(3) - adbClick_wbb(200, 400) - Delay(3) - adbClick_wbb(200, 375) - Delay(3) - adbClick_wbb(142, 159) - Delay(3) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for Month`n(" . failSafeTime . "/45 seconds)") - } ;select month and year and click - - Delay(3) - FindImageAndClick("Create_BirthConfirmCancelButton", 140, 474, , 1000) - - ;wait date confirmation screen while clicking ok - - FindImageAndClick("Create_TosOpenButton", 203, 371, , 1000) ;wait to be at the tos screen while confirming birth - - FindImageAndClick("Create_TosCloseButton", 139, 299, , 1000) ;wait for tos while clicking it - - FindImageAndClick("Create_TosOpenButton", 142, 486, , 1000) ;wait to be at the tos screen and click x - - FindImageAndClick("Common_PopupXButtonInMain", 142, 339, , 1000) ;wait to be at the tos screen - - FindImageAndClick("Create_TosOpenButton", 142, 486, , 1000) ;wait to be at the tos screen, click X - - Delay(3) - adbClick_wbb(261, 374) - - Delay(3) - adbClick_wbb(261, 406) - - Delay(3) - adbClick_wbb(145, 484) - - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - if(FindOrLoseImage("Create_SignupNotification", 0,failSafeTime)) { - ; notification flow - adbClick(138, 434) - Sleep, 1000 - adbClick(136, 350) - Sleep, 1000 - adbClick(136, 350) - Delay(1) - } - if(FindOrLoseImage("Create_SignupsDataSharing", 0, failSafeTime)) { - adbClick(82, 287) - Sleep, 1000 - adbClick(80, 355) - Sleep, 100 - adbClick(90, 425) - Sleep, 100 - adbClick(141, 484) - Delay(1) - } - if(FindImageAndClick("Create_BeginNewAccountButton", 145, 484, , , 2, failSafeTime)) ;wait to be at create save data screen while clicking - break - Delay(1) - adbClick_wbb(261, 406) - if(FindImageAndClick("Create_BeginNewAccountButton", 145, 484, , , 2, failSafeTime)) ;wait to be at create save data screen while clicking - break - Delay(1) - adbClick_wbb(261, 374) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for Save`n(" . failSafeTime . "/45 seconds)") - } - - Delay(1) - - adbClick_wbb(143, 348) - - Delay(1) - - failSafeTime := 0 - match := "" - Loop { - if(FindOrLoseImage("Create_NintendoLink", 0, failSafeTime)) { - match :="Create_NintendoLink" - break - } - if(FindOrLoseImage("Create_NintendoLink2", 0, failSafeTime)) { - match :="Create_NintendoLink2" - break - } - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for Nintendo Link`n(" . failSafeTime . "/45 seconds)") - } - Delay(1) - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - if(FindOrLoseImage(match, 0, failSafeTime)){ - adbClick_wbb(140, 460) - Loop { - Delay(1) - if(FindOrLoseImage(match, 1, failSafeTime)){ - adbClick_wbb(140, 380) ; click ok on the interrupted while opening pack prompt - break - } - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - } - } else if(FindOrLoseImage("Create_DownloadAlertWindow", 0, failSafeTime)){ - adbClick_wbb(203, 364) - } else if(FindOrLoseImage("Create_DownloadComplete", 0, failSafeTime)){ - adbClick_wbb(140, 370) - } else if(FindOrLoseImage("Create_CinematicBackground", 0, failSafeTime)){ - break - } - Delay(1) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - } - - if(session.get("setSpeed") = 3){ - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - FindImageAndClick(GetSpeedModNeedle(1), GetSpeedModClickX(1), GetSpeedModClickY(1)) - Delay(1) - adbClick_wbb(51, 297) - Delay(1) - } - - FindImageAndClick("Create_WelcomePopup", 253, 506, , 110) ;click through cutscene until welcome page - - if(session.get("setSpeed") = 3){ - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) - Delay(1) - adbClick_wbb(51, 297) - Delay(1) - } - FindImageAndClick("Create_NameInputIcon", 189, 438) ;wait for name input screen - FindImageAndClick("Create_DeactivatedOKButton", 139, 257) ;wait for name input screen - - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - if (botConfig.get("AccountName") != "ERROR" && botConfig.get("AccountName") != "") { - Random, randomNum, 1, 500 ; Generate random number from 1 to 500 - username := botConfig.get("AccountName") . "-" . randomNum - username := SubStr(username, 1, 14) ; max character limit - LogDebug("Using AccountName: " . username) - } else { - fileName := A_ScriptDir . "\..\usernames.txt" - if(FileExist(fileName)) - name := ReadFile("usernames") - else - name := ReadFile("usernames_default") - - Random, randomIndex, 1, name.MaxIndex() - username := name[randomIndex] - username := SubStr(username, 1, 14) ; max character limit - LogDebug("Using random username: " . username) - } - - adbInput(username) - Delay(1) - if(FindImageAndClick("Create_PackReturnButtonIcon", 185, 372, , , 10)) - break - adbClick_wbb(90, 370) - Delay(1) - adbClick_wbb(139, 254) ; 139 254 194 372 - Delay(1) - adbClick_wbb(139, 254) - Delay(1) - EraseInput() ; incase the random pokemon is not accepted - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("In failsafe for Trace. " . failSafeTime . "/45 seconds") - if(failSafeTime > 45) - restartGameInstance("Stuck at name") - } - - Delay(1) - - adbClick_wbb(140, 424) - - FindImageAndClick("Pack_ReadyForOpenPack", 140, 424) ;wait for pack to be ready to trace - if(session.get("setSpeed") > 1) { - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - FindImageAndClick(GetSpeedModNeedle(1), GetSpeedModClickX(1), GetSpeedModClickY(1)) - Delay(1) - adbClick_wbb(51, 297) - Delay(1) - } - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - adbSwipe_wbb(adbSwipeParams) - Sleep, 100 - if(FindOrLoseImage("Pack_ReadyForOpenPack", 1, failSafeTime)){ - if(session.get("setSpeed") > 1) { - if(session.get("setSpeed") = 3) { - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) ; click 3x - } - } - adbClick_wbb(51, 297) - break - } - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for Pack`n(" . failSafeTime . "/45 seconds)") - } - - FindImageAndClick("Create_SwipeForRegisterDexIcon", 140, 375) ;click through cards until needing to swipe up - if(session.get("setSpeed") > 1) { - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - FindImageAndClick(GetSpeedModNeedle(1), GetSpeedModClickX(1), GetSpeedModClickY(1)) - Delay(1) - adbClick_wbb(51, 297) - Delay(1) - } - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - adbSwipe_wbb("266 770 266 355 60") - Sleep, 100 - if(FindOrLoseImage("Create_ConfirmRegisteredCard", 0, failSafeTime)){ - if(session.get("setSpeed") > 1) { - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - if(session.get("setSpeed") = 3) - FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) - else - FindImageAndClick(GetSpeedModNeedle(2), GetSpeedModClickX(2), GetSpeedModClickY(2)) - } - adbClick_wbb(51, 297) - break - } - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for swipe up for " . failSafeTime . "/45 seconds") - Delay(1) - } - - Delay(1) - adbClick_wbb(204, 371) - - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - adbClick_wbb(137, 365) - Delay(1) - adbClick_wbb(137, 480) - Delay(1) - if(FindOrLoseImage("Create_MustClickMissionBackground", 0, failSafeTime)){ - break - } else if(FindOrLoseImage("Create_DownloadAlertWindow", 0, failSafeTime)){ - adbClick_wbb(203, 364) - } - } - - Delay(1) - adbClick_wbb(247, 472) - - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - if(FindOrLoseImage("Create_TutorialPackOpenNotifyIcon", 0, failSafeTime)) { - break - } - adbClick_wbb(90, 260) - adbClick_wbb(140, 400) - adbClick_wbb(137, 340) - Delay(3) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for pack notification " . failSafeTime . "/45 seconds") - } - - FindImageAndClick("Create_TutorialPackOpenNotifyIcon", 145, 194) ;click on packs. stop at booster pack tutorial - - Delay(3) - adbClick_wbb(142, 436) - Delay(3) - adbClick_wbb(142, 436) - Delay(3) - adbClick_wbb(142, 436) - Delay(3) - adbClick_wbb(142, 436) - - FindImageAndClick("Pack_ReadyForOpenPack", 239, 497) ;wait for pack to be ready to Trace - if(session.get("setSpeed") > 1) { - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - FindImageAndClick(GetSpeedModNeedle(1), GetSpeedModClickX(1), GetSpeedModClickY(1)) - Delay(1) - adbClick_wbb(51, 297) - Delay(1) - } - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - adbSwipe_wbb(adbSwipeParams) - Sleep, 100 - if(FindOrLoseImage("Pack_ReadyForOpenPack", 1, failSafeTime)){ - if(session.get("setSpeed") > 1) { - if(session.get("setSpeed") = 3) { - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) - } - } - adbClick_wbb(51, 297) - break - } - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for Pack`n(" . failSafeTime . "/45 seconds)") - Delay(1) - } - - FindImageAndClick("Pack_ResultAfterOpenPack", 252, 505, 5, 50) ;skip through cards until results opening screen - - FindImageAndClick("Pack_SkipButtonAfterOpenPack", 146, 496) ;click on next until skip button appears - - FindImageAndClick("Pack_NextButtonAfterOpenPack", 239, 497, , , 2) - - FindImageAndClick("Create_UnlockedWonerPickIconInLevelUp", 146, 494) ;click on next until skip button appearsstop at hourglasses tutorial - - Delay(3) - - adbClick_wbb(140, 358) - - FindImageAndClick("Common_ShopButtonInMain", 146, 444) ;click until at main menu - - ; New needle & search region 11.1.2025 kevinnnn - FindImageAndClick("Create_CardImageInTutorialWPFirstScreen", 79, 411) - - FindImageAndClick("Create_WPItemBottomBorder", 190, 437) ; click through tutorial - - Delay(2) - - adbClick_wbb(202, 347) ; select Wonder Pick item to open Bonus Pick confirm - Delay(2) - - ; Bonus Pick confirm (No cost / OK). Wonder4 selection needle often never matches on current UI. - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - adbClick_wbb(208, 461) ; OK - Delay(1) - if(FindOrLoseImage("Create_TitleBottomBorderInWPSelectCard", 0, failSafeTime)) - break - ; Dialog may not have opened yet — retry select - adbClick_wbb(202, 347) - Delay(1) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Confirming WonderPick`n(" . failSafeTime . "/45 seconds)") - } - - if(session.get("setSpeed") = 3) ;time the animation - Sleep, 1500 - else - Sleep, 2500 - - Delay(1) - - adbClick_wbb(187, 345) - - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - if(session.get("setSpeed") = 3) - continueTime := 1 - else - continueTime := 3 - - if (TryWonderPickDexSwipeRegister()) { - failSafeTime := 0 - } else if(FindOrLoseImage("Pack_SkipButtonAfterOpenPack", 0, failSafeTime)) { - adbClick_wbb(239, 497) - } else if(FindOrLoseImage("Create_WelcomePopup", 0, failSafeTime)) { ;click through to end of tut screen - break - } else if(FindOrLoseImage("Pack_NextButtonAfterOpenPack", 0, failSafeTime)) { - adbClick_wbb(146, 494) ;146, 494 - } else if(FindOrLoseImage("Next2", 0, failSafeTime)) { - adbClick_wbb(146, 494) ;146, 494 - } else { - adbClick_wbb(187, 345) - Delay(1) - adbClick_wbb(143, 492) - Delay(1) - adbClick_wbb(143, 492) - Delay(1) - } - Delay(1) - - ; adbClick_wbb(66, 446) - ; Delay(1) - ; adbClick_wbb(66, 446) - ; Delay(1) - ; adbClick_wbb(66, 446) - ; Delay(1) - ; adbClick_wbb(187, 345) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for End`n(" . failSafeTime . "/45 seconds)") - } - - FindImageAndClick("Create_FullFreepackInMainCenter", 192, 449) ;click until at main menu - EnterGameFromWelcomeIfNeeded() ; 1.7.0 forces restart after WP onto Welcome before GoToMain - - return true -} - -DismissMainCloseAlertWindow(context := "") { - if(!FindOrLoseImage("Common_CloseAlertWindowInMain", 0, , , true)) - return false - - LogInfo("Dismissed close-alert popup | context=" . context) - if(context != "") - CreateStatusMessage("Closing app quit confirmation`n" . context,,,, false) - else - CreateStatusMessage("Closing app quit confirmation",,,, false) - - adbClick_wbb(75, 365) - Delay(1) - return true -} - -; After create-tutorial WP, 1.7.0 always forces an app restart onto Welcome (Tap to Start). -; ESC there opens quit confirmation — tap to enter instead. -; Returns true once past title screen (home/shop/news X/etc.). -EnterGameFromWelcomeIfNeeded(timeoutSec := 60) { - global session - - session.set("failSafe", A_TickCount) - Loop { - ; Already in-game (including News/X overlays) — let GoToMain use ESC. - if (FindOrLoseImage("Common_ShopButtonInMain", 0, 0, , true) - || FindOrLoseImage("Common_ActivatedHomeInMainMenu", 0, 0, , true) - || FindOrLoseImage("Pack_PackPointButton", 0, 0, , true) - || FindOrLoseImage("Create_FullFreepackInMainCenter", 0, 0, , true) - || FindOrLoseImage("WonderPick_WonderPickButtonInHome", 0, 0, , true) - || FindOrLoseImage("Common_PopupXButtonInMain", 0, 0, , true)) - return true - - if (DismissMainCloseAlertWindow("Welcome gate")) { - Delay(1) - adbClick_wbb(140, 450) ; Tap to Start under quit dialog - Delay(2) - CreateStatusMessage("Entering game from Welcome...") - continue - } - - if (FindOrLoseImage("Create_WelcomePopup", 0, 0, , true) - || FindOrLoseImage("Boot_Welcome", 0, 0, , true) - || FindOrLoseImage("Boot_Welcome2", 0, 0, , true)) { - adbClick_wbb(140, 450) ; Tap to Start - Delay(2) - CreateStatusMessage("Entering game from Welcome...") - continue - } - - Delay(1) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting past Welcome`n(" . failSafeTime . "/" . timeoutSec . " seconds)") - if (failSafeTime >= timeoutSec) - return false - } -} - -; Wonder Pick reveals can show the first-time card-dex register tutorial. -; Skip dismisses it; no swipe needed. -TryWonderPickDexSwipeRegister() { - global session - - if (!FindOrLoseImage("Create_SwipeForRegisterDexIcon", 0, 0, 20, 1)) - return false - - CreateStatusMessage("WonderPick: skip card dex tutorial...") - adbClick_wbb(239, 497) - Delay(1) - session.set("failSafe", A_TickCount) - return true -} - -DoWonderPickOnly() { - global session - - session.set("failSafe", A_TickCount) - failSafeTime := 0 - pickAlreadyRevealed := false - - if (isTerminatePTCGPHelperApp()) { - InitPackOpening() - } - Loop { - if (TryWonderPickDexSwipeRegister()) { - pickAlreadyRevealed := true - break - } - adbClick_wbb(80, 390) ; first wonderpick slot - adbClick_wbb(80, 460) ; backup, second wonderpick slot - if(FindOrLoseImage("WonderPick_NoEnergy", 0, failSafeTime)) { - Sleep, 2000 - CreateStatusMessage("No WonderPick Energy left!",,,, false) - Sleep, 2000 - adbClick_wbb(137, 505) - Sleep, 2000 - adbClick_wbb(35, 515) - Sleep, 4000 - SetWonderPickMetadataFlag() - return - } - if(FindOrLoseImage("WonderPick_WonderPickButtonInHome", 1, failSafeTime)) { - if(FindOrLoseImage("WonderPick_EnergyStatusAfterSelect", 0, failSafeTime)){ - adbClick_wbb(198, 456) - Delay(3) - } - if(FindOrLoseImage("WonderPick_SelectCards", 0, failSafeTime)) - break - } - Delay(1) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for WonderPick`n(" . failSafeTime . "/45 seconds)") - } - if (!pickAlreadyRevealed) { - Sleep, 300 - if(botConfig.get("slowMotion")) - Sleep, 3000 - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - if (TryWonderPickDexSwipeRegister()) { - pickAlreadyRevealed := true - break - } - adbClick_wbb(183, 350) ; click card - if(FindOrLoseImage("WonderPick_SelectCards", 1, failSafeTime)) { - break - } - Delay(1) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for Card`n(" . failSafeTime . "/45 seconds)") - } - } - session.set("failSafe", A_TickCount) - failSafeTime := 0 - ;TODO thanks and wonder pick 5 times for missions - Loop { - if (TryWonderPickDexSwipeRegister()) { - failSafeTime := 0 - continue - } - adbClick_wbb(146, 494) - Delay(1) - if(FindOrLoseImage("Pack_SkipButtonAfterOpenPack", 0, failSafeTime) || FindOrLoseImage("WonderPick_WonderPickButtonInHome", 0, failSafeTime)) - break - if(FindOrLoseImage("WonderPick_SelectCards", 0, failSafeTime)) { - adbClick_wbb(183, 350) ; click card - } - Delay(1) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for Shop`n(" . failSafeTime . "/45 seconds)") - } - - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - Delay(1) - if (TryWonderPickDexSwipeRegister()) { - failSafeTime := 0 - continue - } - if(FindOrLoseImage("Common_ShopButtonInMain", 0, failSafeTime)) - break - else if(FindOrLoseImage("Pack_SkipButtonAfterOpenPack", 0, failSafeTime)) - adbClick_wbb(239, 497) - else if(FindOrLoseImage("WonderPick_SelectCards", 0, failSafeTime)) { - adbClick_wbb(183, 350) ; click card - } - else if(FindOrLoseImage("Common_PopupXButtonInMain", 0, , , true)){ - adbClick_wbb(137, 480) - Delay(1) - } - else - adbInputEvent("111") ;send ESC - Delay(4) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for Shop`n(" . failSafeTime . "/45 seconds)") - } - result := EvaluatePack() - if (result) { - LogToCardDatabase(result) - } - SetWonderPickMetadataFlag() -} - -DoWonderPick() { - global session - - FindImageAndClick("Common_ShopButtonInMain", 40, 515) ;click until at main menu - - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - if(FindOrLoseImage("WonderPick_WonderPickButtonInHome", 0, failSafeTime)) - break - else if(FindOrLoseImage("Common_PopupXButtonInMain", 0, , , true)){ - adbClick_wbb(137, 480) - } - else - adbClick_wbb(59, 429) - Delay(1) - } - - DoWonderPickOnly() - - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - adbClick(261, 478) - Sleep, 1000 - if FindOrLoseImage("Mission_ActivatedBeginnerMissionTabButton", 0, failSafeTime) - break - else if FindOrLoseImage("Mission_GoToDexButtonIcon", 0, failSafeTime) - break - else if FindOrLoseImage("Mission_DailyMissionImage", 0, failSafeTime) - break - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - } - - ;FindImageAndClick("WPMission", 150, 286, , 1000) - FindImageAndClick("Mission_FirstWonderpickMissionIconInDetails", 150, 286, , 1000) - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - Delay(1) - adbClick_wbb(139, 424) - Delay(1) - clickButton := FindOrLoseImage("Common_ColorChangeButton", 0, failSafeTime, 80) - if(clickButton) { - adbClick_wbb(110, 369) - } - else if(FindOrLoseImage("Common_ShopButtonInMain", 1, failSafeTime)){ - GoToMain() - break - } - else if(FindOrLoseImage("Common_PopupXButtonInMain", 0, , , true)){ - adbClick_wbb(137, 480) - Delay(1) - } - else - break - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Waiting for WonderPick`n(" . failSafeTime . "/45 seconds)") - } - return true -} - -;------------------------------------------------------------------------------- -; ClaimAllMissionRewards - Unified single-pass claim for Daily + Special missions. -; Handles the full Special-event lifecycle: AdvanceSpecialEventSteps, Elite Deck -; claims and per-event metadata (claimCount). X remains legacy status metadata -; and does not decide whether an active event can be claimed. -; Returns {daily: bool, special: bool, eliteDeckRestart: bool}. -;------------------------------------------------------------------------------- -ClaimAllMissionRewards(claimDaily := false, claimSpecial := false, accountMeta := "") { - global botConfig, session - - if (!claimDaily && !claimSpecial) - return {daily: false, special: false, eliteDeckRestart: false} - - method := botConfig.get("deleteMethod") - - ; --- Special-mission eligibility & advance --- - specialEligible := false - if (claimSpecial) { - if (botConfig.get("claimSpecialMissions") = 1 - && !session.get("missionDoneList")["specialMissionsDone"] - && (method = "Inject 13P+" || method = "Inject Wonderpick 96P+" || method = "Inject Rewards")) { - - if (!IsObject(accountMeta) && session.get("injectMethod") && session.get("loadedAccount") && session.get("accountFileName") != "") { - accountMetaPath := A_ScriptDir "\..\Accounts\Saved\" . session.get("scriptName") . "\" . session.get("accountFileName") - accountMeta := AccountMetadata_Get(session.get("scriptName"), session.get("accountFileName"), accountMetaPath) - } - - syncSpecialEvents() - - if (!IsObject(accountMeta) || AccountEligibility_NeedsSpecialMissionClaim(accountMeta)) - specialEligible := true - } - } - - session.set("forceReceiveGiftThisRun", 0) - session.set("specialMissionClaimUiEvents", {}) - - advance := {"advancedAny": false, "needClaimUi": false, "forceGift": false, "eliteDeckClaim": false, "claimUiEvents": {}} - if (specialEligible) { - accountMetaPath := "" - if (session.get("injectMethod") && session.get("loadedAccount") && session.get("accountFileName") != "") - accountMetaPath := A_ScriptDir "\..\Accounts\Saved\" . session.get("scriptName") . "\" . session.get("accountFileName") - - if (accountMetaPath != "") - advance := AccountMetadata_AdvanceSpecialEventSteps(session.get("scriptName"), session.get("accountFileName"), accountMetaPath) - - if (advance["forceGift"]) - session.set("forceReceiveGiftThisRun", 1) - if (IsObject(advance["claimUiEvents"])) - session.set("specialMissionClaimUiEvents", advance["claimUiEvents"]) - } - - doSpecial := specialEligible && advance["needClaimUi"] - eliteDeckClaim := advance["eliteDeckClaim"] - - ; --- Navigate to Missions page (single navigation for both Daily + Special) --- - if (!NavigateToMissions()) - return {daily: false, special: false, eliteDeckRestart: false} - - ; --- Elite Deck setup --- - if (doSpecial && eliteDeckClaim) { - adbCommand := session.get("adbPath") . " -s 127.0.0.1:" . session.get("adbPort") - RunWait, % adbCommand . " shell rm -f /data/ptcgp/result.rc", , Hide - RunWait, % adbCommand . " shell rm -f /data/ptcgp/result.log", , Hide - if (isTerminatePTCGPHelperApp()) - InitPackOpening(true) - } - - ; --- Check if all Special events are expired --- - isAllEventExpired := true - if (doSpecial) { - for specialEventName, specialEventObj in session.get("specialEventList") { - if(!specialEventObj.isExpiredSpecialEvent()){ - isAllEventExpired := false - break - } - } - } - - ; --- Single-pass scan loop --- - eventResult := initEventResult() - dailyClaimed := false - specialDone := !doSpecial || isAllEventExpired - eliteDeckRestart := false - - session.set("failSafe", A_TickCount) - failSafeTime := 0 - movedRightCount := 0 - maxMissionPages := 15 - Loop { - if ((!claimDaily || dailyClaimed) && specialDone) - break - - Delay(2) - - ; special case: welcome back screen - if (FindOrLoseImage("Mission_WelcomeBackPreClaim", 0, 0, , true)) { - LogInfo(logContext . ": dismissing WelcomeBackPreClaim", "ADB.txt") - CreateStatusMessage("Welcome Back missions`nDismissing PreClaim...",,,, false) - adbClick_wbb(137, 389) - Delay(1) - } - - ; Check for Daily Missions on this page - if (claimDaily && !dailyClaimed && FindOrLoseImage("Mission_DailyMissionImage", 0, failSafeTime)) { - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - Delay(2) - adbClick(174, 427) - adbClick(174, 427) - Delay(1) - if(FindOrLoseImage("Mission_CompleteGotAllClaims", 0, 0)) { - dailyClaimed := true - break - } - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - if (failSafeTime > 20) - break - } - session.set("failSafe", A_TickCount) - failSafeTime := 0 - continue - } - - ; Check for Special Missions on this page - if (doSpecial && !specialDone) { - ; Elite Deck: park and retry if on Elite Deck page - if (eliteDeckClaim && !isAllEventGotReward(eventResult)) { - eliteDeckVisible := false - for specialEventName, specialEventObj in session.get("specialEventList") { - if (specialEventObj.isEliteDeck && !specialEventObj.isExpiredSpecialEvent()) { - if (specialEventObj.isExistNeedleInScreen(session.get("winTitle")) = 2) { - eliteDeckVisible := true - break - } - } - } - if (eliteDeckVisible) { - session.set("failSafe", A_TickCount) - parkTime := 0 - parked := false - Loop { - claimedEventName := ClaimVisibleEventRewards(eventResult) - if (claimedEventName) { - parked := true - if (session.get("injectMethod") && session.get("loadedAccount") && session.get("accountFileName") != "") { - accountMetaPath := A_ScriptDir "\..\Accounts\Saved\" . session.get("scriptName") . "\" . session.get("accountFileName") - AccountMetadata_BumpSpecialEventClaim(session.get("scriptName"), session.get("accountFileName"), claimedEventName, accountMetaPath) - } - if (HelperHasCardResult()) { - eliteDeckRestart := true - specialDone := true - } - ; already claimed - if (FindOrLoseImage("Mission_CompleteGotAllClaims", 0, failSafeTime, , true)) { - specialDone := true - } - break - } - Delay(1) - parkTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Parking on Elite Deck claim page...`n(" . parkTime . "/45 seconds)") - if (parkTime > 45) - break - } - if (parked) { - session.set("failSafe", A_TickCount) - failSafeTime := 0 - if (eliteDeckRestart) - break - if (isAllEventGotReward(eventResult)) - specialDone := true - continue - } - } - } - - claimedEventName := ClaimVisibleEventRewards(eventResult) - if (claimedEventName) { - session.set("failSafe", A_TickCount) - failSafeTime := 0 - if (session.get("injectMethod") && session.get("loadedAccount") && session.get("accountFileName") != "") { - accountMetaPath := A_ScriptDir "\..\Accounts\Saved\" . session.get("scriptName") . "\" . session.get("accountFileName") - AccountMetadata_BumpSpecialEventClaim(session.get("scriptName"), session.get("accountFileName"), claimedEventName, accountMetaPath) - } - if (isAllEventGotReward(eventResult)) - specialDone := true - continue - } - } - - ; Wrapped around (Premium Lock = full loop completed) - if(FindOrLoseImage("Mission_PremiumLockImage", 0, failSafeTime)) - break - - movedRightCount++ - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Scanning mission rewards page " . movedRightCount . "`n(" . failSafeTime . "/60 seconds)") - adbClick_wbb(197, 459) - Delay(0.2) - adbClick_wbb(175, 445) - Delay(3) - - if (failSafeTime > 60 || movedRightCount >= maxMissionPages) - break - } - - ; --- Elite Deck restart: finish claim then re-run for remaining rewards --- - if (eliteDeckRestart && eliteDeckClaim && HelperHasCardResult()) { - eliteDeckResult := ReadEliteDeckResult(45) - FinishEliteDeckClaim(eliteDeckResult) - return ClaimAllMissionRewards(false, claimSpecial, accountMeta) - } - - ; --- Post-claim metadata for Special missions --- - if (specialEligible) { - session.get("missionDoneList")["specialMissionsDone"] := 1 - session.set("cantOpenMorePacks", 0) - - if (advance["advancedAny"] && session.get("injectMethod") && session.get("loadedAccount") && session.get("accountFileName") != "") { - accountMetaPath := A_ScriptDir "\..\Accounts\Saved\" . session.get("scriptName") . "\" . session.get("accountFileName") - accountMeta := AccountMetadata_Get(session.get("scriptName"), session.get("accountFileName"), accountMetaPath) - AccountMetadata_ApplySpecialMissionXFlag(session.get("scriptName"), session.get("accountFileName"), accountMeta) - setMetaData() - } - } - - return {daily: dailyClaimed, special: specialEligible, eliteDeckRestart: false} -} - -;------------------------------------------------------------------------------- -; NavigateToMissions - Navigate from Main to the Missions page and activate the List tab. -; Returns true if the missions page was reached, false on timeout. -;------------------------------------------------------------------------------- -NavigateToMissions() { - global session - - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - adbClick(261, 478) - Delay(1) - if (FindOrLoseImage("Mission_ActivatedBeginnerMissionTabButton", 0, failSafeTime) - || FindOrLoseImage("Mission_ActivatedListTabButton", 0, failSafeTime) - || FindOrLoseImage("Mission_GoToDexButtonIcon", 0, failSafeTime) - || FindOrLoseImage("Mission_DailyMissionImage", 0, failSafeTime)) - break - if (FindOrLoseImage("MissionDeck", 0, failSafeTime)) { - HandleMissionDeckFailsafe() - return false - } - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - if (failSafeTime > 60) { - restartGameInstance("Stuck navigating to Missions") - return false - } - CreateStatusMessage("Moving to Missions...(" . failSafeTime . "/60 seconds)") - } - - ; Activate the List tab and confirm ListActivated is visible for 2 seconds - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop { - if (FindOrLoseImage("Mission_ActivatedListTabButton", 0, 0)) { - confirmedStart := A_TickCount - Loop { - if (!FindOrLoseImage("Mission_ActivatedListTabButton", 0, 0)) { - confirmedStart := 0 - break - } - if (A_TickCount - confirmedStart >= 2000) - return true - Delay(0.5) - } - } - Delay(1) - adbClick_wbb(31, 495) - Delay(2) - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - if (failSafeTime > 20) { - LogWarn("NavigateToMissions: timed out waiting for ListActivated") - return false - } - CreateStatusMessage("Activating List tab...(" . failSafeTime . "/20 seconds)") - } - - return true -} - -; After claiming an Elite Deck event or opening Gift packs: register the cards -; with the Helper (no Discord notification), then restart the game so their -; in-game registration is instant, and go back to Main so the run resumes. -; helperResult may already contain the parsed Helper result; if not, we try to -; recover it with a pre/post restart diff. -FinishEliteDeckClaim(helperResult := false, contextName := "Elite Deck", failedPrefix := "elitedeck") { - global session - - CreateStatusMessage("Registering " . contextName . " cards...",,,, false) - - ; Restart the game first: the cards are only reliably written to - ; MissionUserPrefs after the app boots back up and syncs with the server. - ; The pre-claim snapshot was already saved by InitPackOpening(true). - TerminateHelper() - CreateStatusMessage("Restarting game to speed up " . contextName . " registration...",,,, false) - closePTCGPApp() - Sleep, 100 - startPTCGPApp() - - ; Treat the restart as a boot gate: reuse the same sequence used when - ; loading an injected account (SpeedMod menu), then run the normal - ; startPreProcess so the game reaches the expected main screen. - waitForAppBootScreen() - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - if(session.get("setSpeed") = 3) - FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) - else - FindImageAndClick(GetSpeedModNeedle(2), GetSpeedModClickX(2), GetSpeedModClickY(2)) - Delay(1) - adbClick_wbb(51, 297) - Delay(1) - ; Elite Deck restart must always land at Home, regardless of bot mode. - startPreProcess("Inject Rewards") - - ; After the restart, give the game time to sync the cards into - ; MissionUserPrefs before capturing the post-restart snapshot. - Sleep, 3000 - - ; Use the Helper result captured during the claim if it is already parseable. - ; Otherwise fall back to a pre/post restart diff of MissionUserPrefs. - result := helperResult - if (!result) { - SavePackOpeningMissionUserPrefsSnapshot("post") - result := RecoverPack() - } - if (result) { - LogToCardDatabase(result) - AddShinedustToDatabase(result.shinedust) - LogInfo("Instance: " . session.get("scriptName") . " | " . contextName . " cards stored to card database") - } else { - ; Save diagnostics (no Discord spam) so we can inspect why the diff failed. - failedDir := getScriptBaseFolder() . "\Logs\failed" - uniquePrefix := A_Now . "_" . session.get("scriptName") . "_" . failedPrefix - PullPackOpeningMissionUserPrefsSnapshot("pre", failedDir, uniquePrefix) - PullPackOpeningMissionUserPrefsSnapshot("post", failedDir, uniquePrefix) - PullPackOpeningResultLog(failedDir, uniquePrefix) - LogWarn("Instance: " . session.get("scriptName") . " | " . contextName . " card recognition failed; diagnostics saved to Logs\failed with prefix " . uniquePrefix) - } - GoToMain() -} - -ClaimVisibleEventRewards(eventResult) { - global session - - claimUiEvents := session.get("specialMissionClaimUiEvents") - hasClaimFilter := IsObject(claimUiEvents) - if (hasClaimFilter) { - filterCount := 0 - for k, v in claimUiEvents - filterCount++ - if (filterCount < 1) - hasClaimFilter := false - } - - for specialEventName, specialEventObj in session.get("specialEventList") { - if(eventResult.HasKey(specialEventName) && eventResult[specialEventName]) - continue - if(specialEventObj.isExpiredSpecialEvent()){ - eventResult[specialEventName] := true - continue - } - ; Only attempt claim UI for events advanced onto a ClaimDay this run. - if (hasClaimFilter && !claimUiEvents.HasKey(specialEventName)) { - eventResult[specialEventName] := true - continue - } - - if (specialEventObj.isExistNeedleInScreen(session.get("winTitle")) = 2){ - session.set("failSafe", A_TickCount) - failSafeTime := 0 - Loop{ - adbClick_wbb(175, 422) - Delay(1) - adbClick_wbb(138, 451) - Delay(1) - - ; Elite Deck events: do not wait for the usual claim-complete needle. - ; The Helper is already watching MissionUserPrefs; as soon as it writes - ; result.rc the claim has been processed and we can proceed to the - ; restart + card registration in FinishEliteDeckClaim. - if (specialEventObj.isEliteDeck) { - if (HelperHasCardResult()) { - eventResult[specialEventName] := true - return specialEventName - } - } - if (FindOrLoseImage("Mission_CompleteGotAllClaims", 0, failSafeTime, , true)) { - eventResult[specialEventName] := true - return specialEventName - } - - failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 - CreateStatusMessage("Get reward event: " . specialEventName . "`n(" . failSafeTime . "/45 seconds)") - if (failSafeTime > 45) { - ; For Elite Deck, do not mark the event as claimed until the - ; Helper actually reports a card result. Returning false lets - ; GetEventRewards keep retrying or scrolling instead of giving up. - if (!specialEventObj.isEliteDeck) - eventResult[specialEventName] := true - return false - } - } - } - } - - return false -} - -; Failsafe if Missions page lands on 'Deck' mission tutorial. -HandleMissionDeckFailsafe() { - Sleep, 500 - adbInput("111") ; ESC - Sleep, 500 - adbInput("111") ; ESC - Sleep, 500 - adbInput("111") ; ESC - Sleep, 500 - adbInput("111") ; ESC - Sleep, 500 - adbClick(146,438) - Sleep, 1500 - adbInput("111") ; ESC to home screen - Sleep, 1000 - return true -} - -TryDismisMyCardsTutorial(failSafeTime) { - if (!FindOrLoseImage("Common_BlurryMyCards", 0, , , true)) { - return false - } - - adbClick(168, 433) - Delay(1) - adbClick(168, 433) - Delay(1) - adbClick(168, 433) - Delay(10) - - adbClick(168, 471) - Delay(1) - adbClick(168, 471) - Delay(1) - adbClick(168, 471) - Delay(1) - adbClick(168, 471) - Delay(10) - - adbClick(168, 432) - Delay(10) -} - GoToMain() { global session diff --git a/Scripts/Include/BotAccountMenu.ahk b/Scripts/Include/BotAccountMenu.ahk new file mode 100644 index 00000000..37400f1b --- /dev/null +++ b/Scripts/Include/BotAccountMenu.ahk @@ -0,0 +1,149 @@ +; In-game account deletion and injected god pack finalization + +FinalizeInjectedGodPackAccount(testing := True) { + global botConfig, session, DeadCheck + + if (!session.get("injectMethod") || !session.get("loadedAccount")) + return + + if(testing) { + session.get("missionDoneList")["accountHasPackInTesting"] := 1 + } + setMetaData() + IniWrite, 0, % session.get("scriptIniFile"), UserSettings, DeadCheck + + if (botConfig.get("deleteMethod") = "Inject 13P+" || botConfig.get("deleteMethod") = "Inject Wonderpick 96P+") { + MarkAccountAsUsed() + session.set("loadedAccount", false) + } +} + +menuDelete() { + prof := Prof_Scope(A_ThisFunc) + global botConfig, session + + Delay(1) + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop + { + Delay(2) + adbClick_wbb(245, 518) + if(FindImageAndClick("Menu_InventoryIconInMenu", , , , , 1, failSafeTime)) ;wait for settings menu + break + Delay(2) + adbClick_wbb(50, 100) + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting for Settings`n(" . failSafeTime . " seconds)") + } + Delay(1) + FindImageAndClick("Menu_SettingButtonInMenu", 140, 440, , 2000) ;wait for other menu + Delay(1) + FindImageAndClick("Menu_RemoveAccountNintendoButtonInMenu", 79, 256, , 1000) ;wait for account menu + Delay(1) + + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + clickButton := FindOrLoseImage("Common_UnknownButton2", 0, failSafeTime, 40) + if(!clickButton) { + ; fix https://discord.com/channels/1330305075393986703/1354775917288882267/1355090394307887135 + clickImage := FindOrLoseImage("Menu_DeleteConfimButtonStep1", 0, failSafeTime, 60) + if(clickImage) { + StringSplit, pos, clickImage, `, ; Split at ", " + adbClick_wbb(pos1, pos2) + } + else { + adbClick_wbb(198, 480) + Delay(2) + adbClick_wbb(230, 506) + } + Delay(1) + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting to click delete`n(" . failSafeTime . "/45 seconds)") + } + else { + break + } + Delay(1) + } + StringSplit, pos, clickButton, `, ; Split at ", " + adbClick_wbb(pos1, pos2) + break + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting to click delete`n(" . failSafeTime . "/45 seconds)") + } + + Sleep, 2500 +} + +menuDeleteStart() { + prof := Prof_Scope(A_ThisFunc) + global botConfig, session + + if(session.get("keepAccount")) { + return session.get("keepAccount") + } + if(session.get("friended")) { + FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) + if(session.get("setSpeed") = 3) + FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) + else + FindImageAndClick(GetSpeedModNeedle(2), GetSpeedModClickX(2), GetSpeedModClickY(2)) + Delay(1) + adbClick_wbb(51, 297) + Delay(1) + } + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + if(!session.get("friended")) + break + adbClick_wbb(255, 83) + if(FindOrLoseImage("Create_CountryComboBoxButton", 0, failSafeTime)) { ;if at country continue + break + } + else if(FindOrLoseImage("Menu_AgreementIconInIntroMenu", 0, failSafeTime)) { ; if the clicks in the top right open up the game settings menu then continue to delete account + Delay(1) + FindImageAndClick("Menu_RemoveAccountNintendoButtonInMenu", 79, 256, , 1000) ;wait for account menu + Delay(1) + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + clickButton := FindOrLoseImage("Common_ColorChangeButton", 0, failSafeTime, 80) + if(!clickButton) { + clickImage := FindOrLoseImage("Menu_DeleteConfimButtonStep1", 0, failSafeTime, 60) + if(clickImage) { + StringSplit, pos, clickImage, `, ; Split at ", " + adbClick_wbb(pos1, pos2) + } + else { + adbClick_wbb(230, 506) + } + Delay(1) + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting to click delete`n(" . failSafeTime . "/45 seconds)") + } + else { + break + } + Delay(1) + } + StringSplit, pos, clickButton, `, ; Split at ", " + adbClick_wbb(pos1, pos2) + break + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting to click delete`n(" . failSafeTime . "/45 seconds)") + } + CreateStatusMessage("Looking for Country/Menu") + Delay(1) + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting for Country/Menu`n(" . failSafeTime . "/45 seconds)") + } + if(session.get("loadedAccount")) { + ; FileDelete, % session.get("loadedAccount") + } +} diff --git a/Scripts/Include/BotDebugTools.ahk b/Scripts/Include/BotDebugTools.ahk new file mode 100644 index 00000000..0cd746b1 --- /dev/null +++ b/Scripts/Include/BotDebugTools.ahk @@ -0,0 +1,285 @@ +; Screenshots and swipe debugging helpers + +SaveStuckScreenshot(reason) { + prof := Prof_Scope(A_ThisFunc) + global session + + fileDir := A_ScriptDir . "\..\Screenshots\Stuck" + if !FileExist(fileDir) + FileCreateDir, %fileDir% + + safeReason := RegExReplace(reason, "[\\/:*?""<>|]", "_") + safeReason := RegExReplace(safeReason, "\s+", "_") + safeReason := SubStr(safeReason, 1, 80) + + deviceAccount := GetCurrentDeviceAccountForMetadata() + if (deviceAccount = "") + deviceAccount := "unknown_device_account" + safeDeviceAccount := RegExReplace(deviceAccount, "[\\/:*?""<>|]", "_") + safeDeviceAccount := RegExReplace(safeDeviceAccount, "\s+", "_") + safeDeviceAccount := SubStr(safeDeviceAccount, 1, 80) + + filePath := fileDir . "\" . A_Now . "_inst" . session.get("scriptName") . "_" . safeDeviceAccount . "_" . safeReason . ".png" + + hWnd := getMuMuHwnd(session.get("winTitle")) + if (!hWnd) + return + + pBitmap := from_window(hWnd) + if (pBitmap) { + Gdip_SaveBitmapToFile(pBitmap, filePath) + Gdip_DisposeImage(pBitmap) + LogInfo("Saved stuck screenshot: " . filePath) + } +} + +Screenshot_dev(fileType := "Dev", subDir := "", srcPath := "") { + prof := Prof_Scope(A_ThisFunc) + global session, rec_Active + SetWorkingDir %A_ScriptDir% ; Ensures the working directory is the script's directory + + ; Define folder and file paths + fileDir := A_ScriptDir "\..\Screenshots\grab" + if !FileExist(fileDir) + FileCreateDir, %fileDir% + if (subDir) { + fileDir .= "\" . subDir + } + if !FileExist(fileDir) + FileCreateDir, %fileDir% + + ; File path for saving the screenshot locally + fileName := A_Now . "_" . session.get("scriptName") . "_" . fileType . ".png" + filePath := fileDir "\" . fileName + + if (srcPath != "" && FileExist(srcPath)) + pBitmapW := Gdip_CreateBitmapFromFile(srcPath) + else + pBitmapW := from_window(getMuMuHwnd(session.get("winTitle"))) + Gdip_SaveBitmapToFile(pBitmapW, filePath) + + sleep 100 + + try { + CoordMode, Mouse, Client + OwnerWND := WinExist(session.get("winTitle")) + buttonWidth := 40 + + guiSuffix := session.get("winTitle") + Gui, DevMode_ss%guiSuffix%:New, +LastFound -DPIScale + Gui, DevMode_ss%guiSuffix%:Add, Picture, x0 y0 w275 h528 hwndhAppScreen, %filePath% + Gui, DevMode_ss%guiSuffix%:Show, w275 h528, % "Screensho" session.get("winTitle") + + GuiControlGet, PicPos, Pos, %hAppScreen% + + sleep 100 + msgbox click on top-left corner and bottom-right corners + + yBias := 40 + + KeyWait, LButton, D + MouseGetPos , X1, Y1, OutputVarWin, OutputVarControl + KeyWait, LButton, U + X1 := X1 - PicPosX + Y1 := Y1 - PicPosY + + ; Return in case of user close the screen + if !WinExist("Screensho" session.get("winTitle")) + return + + KeyWait, LButton, D + MouseGetPos , X2, Y2, OutputVarWin, OutputVarControl + KeyWait, LButton, U + X2 := X2 - PicPosX + Y2 := Y2 - PicPosY + + W:=X2-X1 + H:=Y2-Y1 + + MsgBox, % X1 ", " Y1 " / " X2 ", " Y2 + + pBitmap := Gdip_CloneBitmapArea(pBitmapW, X1, Y1, W, H) + + InputBox, fileName, ,"Enter the name of the needle to save" + + if (fileName = "") { + return "" + } + + fullScreenPath := filePath + fileDir := A_ScriptDir . "\Needles" + filePath := fileDir "\" . fileName . ".png" + Gdip_SaveBitmapToFile(pBitmap, filePath) + + KeyWait, LButton, D + MouseGetPos , X3, Y3, OutputVarWin, OutputVarControl + KeyWait, LButton, U + X3 := X3 - PicPosX + Y3 := Y3 - PicPosY + + global rec_LastScreenGrab + rec_LastScreenGrab := {fileName: fileName, needlePath: filePath + , screenshot: fullScreenPath + , x1: X1, y1: Y1, x2: X2, y2: Y2, x3: X3, y3: Y3} + + ; Convert window coordinates to device/OCR coordinates + ; Device resolution: 540x960, Window resolution: 277x489, Y offset: 44 + OCR_X1 := Round(X1 * 540 / 283) + OCR_Y1 := Round((Y1 - 44) * 960 / 488) + OCR_W := Round(W * 540 / 283) + OCR_H := Round(H * 960 / 488) + OCR_X2 := OCR_X1 + OCR_W + OCR_Y2 := OCR_Y1 + OCR_H + + ; Calculate center point of the box + OCR_X3 := Round(OCR_X1 + OCR_W / 2) + OCR_Y3 := Round(OCR_Y1 + OCR_H / 2) + + MsgBox, + (LTrim + ctrl+C to copy: + FindOrLoseImage(%X1%, %Y1%, %X2%, %Y2%, , "%fileName%", 0, failSafeTime) + FindImageAndClick(%X1%, %Y1%, %X2%, %Y2%, , "%fileName%", %X3%, %Y3%, sleepTime) + adbClick_wbb(%X3%, %Y3%) + OCR coordinates: %OCR_X3%, %OCR_Y3%, %OCR_W%, %OCR_H% + ) + } + catch { + msgbox Failed to create screenshot GUI + } + CoordMode, Pixel, Screen + return filePath +} + +Screenshot(fileType := "Valid", subDir := "", ByRef fileName := "") { + prof := Prof_Scope(A_ThisFunc) + SetWorkingDir %A_ScriptDir% ; Ensures the working directory is the script's directory + + ; Define folder and file paths + fileDir := A_ScriptDir "\..\Screenshots" + if !FileExist(fileDir) + FileCreateDir, %fileDir% + if (subDir) { + fileDir .= "\" . subDir + if !FileExist(fileDir) + FileCreateDir, %fileDir% + } + if (filename = "PACKSTATS") { + fileDir .= "\temp" + if !FileExist(fileDir) + FileCreateDir, %fileDir% + } + + ; File path for saving the screenshot locally + fileName := A_Now . "_" . session.get("scriptName") . "_" . fileType . "_" . session.get("packsInPool") . "_packs.png" + if (filename = "PACKSTATS") + fileName := "packstats_temp.png" + filePath := fileDir "\" . fileName + + yBias := 40 + cropX := 18 + cropY := 170 + cropW := 240 + cropH := 227 + + if (fileType = "FRIENDCODE"){ + cropX := 18 + cropY := 66 + cropW := 240 + cropH := 165 + } + + pBitmapW := from_window(getMuMuHwnd(session.get("winTitle"))) + pBitmap := Gdip_CloneBitmapArea(pBitmapW, cropX, cropY, cropW, cropH) + + Gdip_DisposeImage(pBitmapW) + Gdip_SaveBitmapToFile(pBitmap, filePath) + + ; Don't dispose pBitmap if it's a PACKSTATS screenshot + if (filename != "PACKSTATS") { + Gdip_DisposeImage(pBitmap) + return filePath + } + + ; For PACKSTATS, return both values and delete temp file after OCR is done + return {filepath: filePath, bitmap: pBitmap, deleteAfterUse: true} +} + +bboxDraw2(X1, Y1, X2, Y2, color) { + global session + + WinGetPos, xwin, ywin, Width, Height, % session.get("winTitle") + BoxWidth := 10 + BoxHeight := 10 + Xm1:=X1-(BoxWidth/2) + Xm2:=X2-(BoxWidth/2) + Ym1:=Y1-(BoxWidth/2) + Ym2:=Y2-(BoxWidth/2) + Xh1:=Xm1+BoxWidth + Xh2:=Xm2+BoxWidth + Yh1:=Ym1+BoxHeight + Yh2:=Ym2+BoxHeight + + ; Create a GUI + guiSuffix := session.get("winTitle") + Gui, BoundingBox%guiSuffix%:+AlwaysOnTop +ToolWindow -Caption +E0x20 + Gui, BoundingBox%guiSuffix%:Color, 123456 + Gui, BoundingBox%guiSuffix%:+LastFound ; Make the GUI window the last found window for use by the line below. (straght from documentation) + WinSet, TransColor, 123456 ; Makes that specific color transparent in the gui + + ; Create the borders and show + Gui, BoundingBox%guiSuffix%:Add, Progress, x%Xm1% y%Ym1% w%BoxWidth% h2 %color% + Gui, BoundingBox%guiSuffix%:Add, Progress, x%Xm1% y%Ym1% w2 h%BoxHeight% %color% + Gui, BoundingBox%guiSuffix%:Add, Progress, x%Xh1% y%Ym1% w2 h%BoxHeight% %color% + Gui, BoundingBox%guiSuffix%:Add, Progress, x%Xm1% y%Yh1% w%BoxWidth% h2 %color% + + ; Create the borders and show + Gui, BoundingBox%guiSuffix%:Add, Progress, x%Xm2% y%Ym2% w%BoxWidth% h2 %color% + Gui, BoundingBox%guiSuffix%:Add, Progress, x%Xm2% y%Ym2% w2 h%BoxHeight% %color% + Gui, BoundingBox%guiSuffix%:Add, Progress, x%Xh2% y%Ym2% w2 h%BoxHeight% %color% + Gui, BoundingBox%guiSuffix%:Add, Progress, x%Xm2% y%Yh2% w%BoxWidth% h2 %color% + + xshow := xwin + yshow := ywin + Gui, BoundingBox%guiSuffix%:Show, x%xshow% y%yshow% NoActivate + Sleep, 100 + +} + +adbSwipe_wbb(params) { + prof := Prof_Scope(A_ThisFunc) + global session + + if(session.get("dbg_bbox")) + bboxAndPause_swipe(params, session.get("dbg_bboxNpause")) + adbSwipe(params) +} + +bboxAndPause_swipe(params, doPause := False) { + global session + + guiSuffix := session.get("winTitle") + paramsplit := StrSplit(params , " ") + X1:=round(paramsplit[1] / 535 * 283) + Y1:=round((paramsplit[2] / 960 * 488) + 40) + X2:=round(paramsplit[3] / 535 * 283) + Y2:=round((paramsplit[4] / 960 * 488) + 40) + speed:=paramsplit[5] + CreateStatusMessage("Swiping (" . X1 . "," . Y1 . ") to (" . X2 . "," . Y2 . ") speed " . speed,,,, false) + + color := "BackgroundYellow" + + ;bboxDraw2(X1, Y1, X2, Y2, color) + + bboxDraw(X1-5, Y1-5, X1+5, Y1+5, color) + if (doPause) { + Pause + } + Gui, BoundingBox%guiSuffix%:Destroy + + bboxDraw(X2-5, Y2-5, X2+5, Y2+5, color) + if (doPause) { + Pause + } + Gui, BoundingBox%guiSuffix%:Destroy +} diff --git a/Scripts/Include/BotMissions.ahk b/Scripts/Include/BotMissions.ahk new file mode 100644 index 00000000..724c74d3 --- /dev/null +++ b/Scripts/Include/BotMissions.ahk @@ -0,0 +1,524 @@ +; Mission navigation and reward claiming + +HomeAndMission(homeonly := 0, completeSecondMisson=false) { + global session + + Sleep, 250 + Leveled := 0 + Loop { + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + FindImageAndClick("Mission_ThemeCollectionButtonIcon", 261, 478, , 1000, 1) + Delay(1) + + if(FindOrLoseImage("Mission_ThemeCollectionButtonIcon", 0, failSafeTime)){ + break + } + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + } + if(!homeonly){ + FindImageAndClick("Mission_ThemeCollectionButtonIcon", 261, 478, , 1000) + + wonderpicked := 0 + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + Delay(1) + + Loop { + if (completeSecondMisson){ + adbClick_wbb(150, 390) + } + else { + adbClick_wbb(150, 286) + } + Delay(1) + + if(FindOrLoseImage("Mission_ThemeCollectionButtonIcon", 1, , 10)){ + break + } + } + + if(FindOrLoseImage("Mission_MissionIconTopAreaInDetails", 0, failSafeTime) || FindOrLoseImage("Mission_MissionIconTopAreaInDetailsAlt", 0, failSafeTime)) + break + + if(FindOrLoseImage("Create_SoloBattleMissionIconInDetail", 0, failSafeTime)) { + SetBeginnerMissionsDone() + return + } + + if (FindOrLoseImage("Mission_FirstWonderpickMissionIconInDetails", 0, failSafeTime)){ + adbClick_wbb(141, 396) ; click try it and go to wonderpick page + DoWonderPickOnly() + wonderpicked := 1 + break + } + + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + } + if(!wonderpicked) + break + } else + break + } + + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + Delay(1) + + adbClick_wbb(139, 424) ;clicks complete mission + Delay(1) + clickButton := FindOrLoseImage("Common_ColorChangeButton", 0, failSafeTime, 80) + if(clickButton) { + adbClick_wbb(110, 435) + Delay(1) + } + else if(FindOrLoseImage("Common_ShopButtonInMain", 1, failSafeTime)) { + GoToMain() + break + } + else + break + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("In failsafe for WonderPick. " . failSafeTime "/45 seconds") + } + return Leveled +} + +;------------------------------------------------------------------------------- +; ClaimAllMissionRewards - Unified single-pass claim for Daily + Special missions. +; Handles the full Special-event lifecycle: AdvanceSpecialEventSteps, Elite Deck +; claims and per-event metadata (claimCount). X remains legacy status metadata +; and does not decide whether an active event can be claimed. +; Returns {daily: bool, special: bool, eliteDeckRestart: bool}. +;------------------------------------------------------------------------------- +ClaimAllMissionRewards(claimDaily := false, claimSpecial := false, accountMeta := "") { + global botConfig, session + + if (!claimDaily && !claimSpecial) + return {daily: false, special: false, eliteDeckRestart: false} + + method := botConfig.get("deleteMethod") + + ; --- Special-mission eligibility & advance --- + specialEligible := false + if (claimSpecial) { + if (botConfig.get("claimSpecialMissions") = 1 + && !session.get("missionDoneList")["specialMissionsDone"] + && (method = "Inject 13P+" || method = "Inject Wonderpick 96P+" || method = "Inject Rewards")) { + + if (!IsObject(accountMeta) && session.get("injectMethod") && session.get("loadedAccount") && session.get("accountFileName") != "") { + accountMetaPath := A_ScriptDir "\..\Accounts\Saved\" . session.get("scriptName") . "\" . session.get("accountFileName") + accountMeta := AccountMetadata_Get(session.get("scriptName"), session.get("accountFileName"), accountMetaPath) + } + + syncSpecialEvents() + + if (!IsObject(accountMeta) || AccountEligibility_NeedsSpecialMissionClaim(accountMeta)) + specialEligible := true + } + } + + session.set("forceReceiveGiftThisRun", 0) + session.set("specialMissionClaimUiEvents", {}) + + advance := {"advancedAny": false, "needClaimUi": false, "forceGift": false, "eliteDeckClaim": false, "claimUiEvents": {}} + if (specialEligible) { + accountMetaPath := "" + if (session.get("injectMethod") && session.get("loadedAccount") && session.get("accountFileName") != "") + accountMetaPath := A_ScriptDir "\..\Accounts\Saved\" . session.get("scriptName") . "\" . session.get("accountFileName") + + if (accountMetaPath != "") + advance := AccountMetadata_AdvanceSpecialEventSteps(session.get("scriptName"), session.get("accountFileName"), accountMetaPath) + + if (advance["forceGift"]) + session.set("forceReceiveGiftThisRun", 1) + if (IsObject(advance["claimUiEvents"])) + session.set("specialMissionClaimUiEvents", advance["claimUiEvents"]) + } + + doSpecial := specialEligible && advance["needClaimUi"] + eliteDeckClaim := advance["eliteDeckClaim"] + + ; --- Navigate to Missions page (single navigation for both Daily + Special) --- + if (!NavigateToMissions()) + return {daily: false, special: false, eliteDeckRestart: false} + + ; --- Elite Deck setup --- + if (doSpecial && eliteDeckClaim) { + adbCommand := session.get("adbPath") . " -s 127.0.0.1:" . session.get("adbPort") + RunWait, % adbCommand . " shell rm -f /data/ptcgp/result.rc", , Hide + RunWait, % adbCommand . " shell rm -f /data/ptcgp/result.log", , Hide + if (isTerminatePTCGPHelperApp()) + InitPackOpening(true) + } + + ; --- Check if all Special events are expired --- + isAllEventExpired := true + if (doSpecial) { + for specialEventName, specialEventObj in session.get("specialEventList") { + if(!specialEventObj.isExpiredSpecialEvent()){ + isAllEventExpired := false + break + } + } + } + + ; --- Single-pass scan loop --- + eventResult := initEventResult() + dailyClaimed := false + specialDone := !doSpecial || isAllEventExpired + eliteDeckRestart := false + + session.set("failSafe", A_TickCount) + failSafeTime := 0 + movedRightCount := 0 + maxMissionPages := 15 + Loop { + if ((!claimDaily || dailyClaimed) && specialDone) + break + + Delay(2) + + ; special case: welcome back screen + if (FindOrLoseImage("Mission_WelcomeBackPreClaim", 0, 0, , true)) { + LogInfo(logContext . ": dismissing WelcomeBackPreClaim", "ADB.txt") + CreateStatusMessage("Welcome Back missions`nDismissing PreClaim...",,,, false) + adbClick_wbb(137, 389) + Delay(1) + } + + ; Check for Daily Missions on this page + if (claimDaily && !dailyClaimed && FindOrLoseImage("Mission_DailyMissionImage", 0, failSafeTime)) { + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + Delay(2) + adbClick(174, 427) + adbClick(174, 427) + Delay(1) + if(FindOrLoseImage("Mission_CompleteGotAllClaims", 0, 0)) { + dailyClaimed := true + break + } + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + if (failSafeTime > 20) + break + } + session.set("failSafe", A_TickCount) + failSafeTime := 0 + continue + } + + ; Check for Special Missions on this page + if (doSpecial && !specialDone) { + ; Elite Deck: park and retry if on Elite Deck page + if (eliteDeckClaim && !isAllEventGotReward(eventResult)) { + eliteDeckVisible := false + for specialEventName, specialEventObj in session.get("specialEventList") { + if (specialEventObj.isEliteDeck && !specialEventObj.isExpiredSpecialEvent()) { + if (specialEventObj.isExistNeedleInScreen(session.get("winTitle")) = 2) { + eliteDeckVisible := true + break + } + } + } + if (eliteDeckVisible) { + session.set("failSafe", A_TickCount) + parkTime := 0 + parked := false + Loop { + claimedEventName := ClaimVisibleEventRewards(eventResult) + if (claimedEventName) { + parked := true + if (session.get("injectMethod") && session.get("loadedAccount") && session.get("accountFileName") != "") { + accountMetaPath := A_ScriptDir "\..\Accounts\Saved\" . session.get("scriptName") . "\" . session.get("accountFileName") + AccountMetadata_BumpSpecialEventClaim(session.get("scriptName"), session.get("accountFileName"), claimedEventName, accountMetaPath) + } + if (HelperHasCardResult()) { + eliteDeckRestart := true + specialDone := true + } + ; already claimed + if (FindOrLoseImage("Mission_CompleteGotAllClaims", 0, failSafeTime, , true)) { + specialDone := true + } + break + } + Delay(1) + parkTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Parking on Elite Deck claim page...`n(" . parkTime . "/45 seconds)") + if (parkTime > 45) + break + } + if (parked) { + session.set("failSafe", A_TickCount) + failSafeTime := 0 + if (eliteDeckRestart) + break + if (isAllEventGotReward(eventResult)) + specialDone := true + continue + } + } + } + + claimedEventName := ClaimVisibleEventRewards(eventResult) + if (claimedEventName) { + session.set("failSafe", A_TickCount) + failSafeTime := 0 + if (session.get("injectMethod") && session.get("loadedAccount") && session.get("accountFileName") != "") { + accountMetaPath := A_ScriptDir "\..\Accounts\Saved\" . session.get("scriptName") . "\" . session.get("accountFileName") + AccountMetadata_BumpSpecialEventClaim(session.get("scriptName"), session.get("accountFileName"), claimedEventName, accountMetaPath) + } + if (isAllEventGotReward(eventResult)) + specialDone := true + continue + } + } + + ; Wrapped around (Premium Lock = full loop completed) + if(FindOrLoseImage("Mission_PremiumLockImage", 0, failSafeTime)) + break + + movedRightCount++ + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Scanning mission rewards page " . movedRightCount . "`n(" . failSafeTime . "/60 seconds)") + adbClick_wbb(197, 459) + Delay(0.2) + adbClick_wbb(175, 445) + Delay(3) + + if (failSafeTime > 60 || movedRightCount >= maxMissionPages) + break + } + + ; --- Elite Deck restart: finish claim then re-run for remaining rewards --- + if (eliteDeckRestart && eliteDeckClaim && HelperHasCardResult()) { + eliteDeckResult := ReadEliteDeckResult(45) + FinishEliteDeckClaim(eliteDeckResult) + return ClaimAllMissionRewards(false, claimSpecial, accountMeta) + } + + ; --- Post-claim metadata for Special missions --- + if (specialEligible) { + session.get("missionDoneList")["specialMissionsDone"] := 1 + session.set("cantOpenMorePacks", 0) + + if (advance["advancedAny"] && session.get("injectMethod") && session.get("loadedAccount") && session.get("accountFileName") != "") { + accountMetaPath := A_ScriptDir "\..\Accounts\Saved\" . session.get("scriptName") . "\" . session.get("accountFileName") + accountMeta := AccountMetadata_Get(session.get("scriptName"), session.get("accountFileName"), accountMetaPath) + AccountMetadata_ApplySpecialMissionXFlag(session.get("scriptName"), session.get("accountFileName"), accountMeta) + setMetaData() + } + } + + return {daily: dailyClaimed, special: specialEligible, eliteDeckRestart: false} +} + +;------------------------------------------------------------------------------- +; NavigateToMissions - Navigate from Main to the Missions page and activate the List tab. +; Returns true if the missions page was reached, false on timeout. +;------------------------------------------------------------------------------- +NavigateToMissions() { + global session + + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + adbClick(261, 478) + Delay(1) + if (FindOrLoseImage("Mission_ActivatedBeginnerMissionTabButton", 0, failSafeTime) + || FindOrLoseImage("Mission_ActivatedListTabButton", 0, failSafeTime) + || FindOrLoseImage("Mission_GoToDexButtonIcon", 0, failSafeTime) + || FindOrLoseImage("Mission_DailyMissionImage", 0, failSafeTime)) + break + if (FindOrLoseImage("MissionDeck", 0, failSafeTime)) { + HandleMissionDeckFailsafe() + return false + } + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + if (failSafeTime > 60) { + restartGameInstance("Stuck navigating to Missions") + return false + } + CreateStatusMessage("Moving to Missions...(" . failSafeTime . "/60 seconds)") + } + + ; Activate the List tab and confirm ListActivated is visible for 2 seconds + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + if (FindOrLoseImage("Mission_ActivatedListTabButton", 0, 0)) { + confirmedStart := A_TickCount + Loop { + if (!FindOrLoseImage("Mission_ActivatedListTabButton", 0, 0)) { + confirmedStart := 0 + break + } + if (A_TickCount - confirmedStart >= 2000) + return true + Delay(0.5) + } + } + Delay(1) + adbClick_wbb(31, 495) + Delay(2) + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + if (failSafeTime > 20) { + LogWarn("NavigateToMissions: timed out waiting for ListActivated") + return false + } + CreateStatusMessage("Activating List tab...(" . failSafeTime . "/20 seconds)") + } + + return true +} + +; After claiming an Elite Deck event or opening Gift packs: register the cards +; with the Helper (no Discord notification), then restart the game so their +; in-game registration is instant, and go back to Main so the run resumes. +; helperResult may already contain the parsed Helper result; if not, we try to +; recover it with a pre/post restart diff. +FinishEliteDeckClaim(helperResult := false, contextName := "Elite Deck", failedPrefix := "elitedeck") { + global session + + CreateStatusMessage("Registering " . contextName . " cards...",,,, false) + + ; Restart the game first: the cards are only reliably written to + ; MissionUserPrefs after the app boots back up and syncs with the server. + ; The pre-claim snapshot was already saved by InitPackOpening(true). + TerminateHelper() + CreateStatusMessage("Restarting game to speed up " . contextName . " registration...",,,, false) + closePTCGPApp() + Sleep, 100 + startPTCGPApp() + + ; Treat the restart as a boot gate: reuse the same sequence used when + ; loading an injected account (SpeedMod menu), then run the normal + ; startPreProcess so the game reaches the expected main screen. + waitForAppBootScreen() + FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) + if(session.get("setSpeed") = 3) + FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) + else + FindImageAndClick(GetSpeedModNeedle(2), GetSpeedModClickX(2), GetSpeedModClickY(2)) + Delay(1) + adbClick_wbb(51, 297) + Delay(1) + ; Elite Deck restart must always land at Home, regardless of bot mode. + startPreProcess("Inject Rewards") + + ; After the restart, give the game time to sync the cards into + ; MissionUserPrefs before capturing the post-restart snapshot. + Sleep, 3000 + + ; Use the Helper result captured during the claim if it is already parseable. + ; Otherwise fall back to a pre/post restart diff of MissionUserPrefs. + result := helperResult + if (!result) { + SavePackOpeningMissionUserPrefsSnapshot("post") + result := RecoverPack() + } + if (result) { + LogToCardDatabase(result) + AddShinedustToDatabase(result.shinedust) + LogInfo("Instance: " . session.get("scriptName") . " | " . contextName . " cards stored to card database") + } else { + ; Save diagnostics (no Discord spam) so we can inspect why the diff failed. + failedDir := getScriptBaseFolder() . "\Logs\failed" + uniquePrefix := A_Now . "_" . session.get("scriptName") . "_" . failedPrefix + PullPackOpeningMissionUserPrefsSnapshot("pre", failedDir, uniquePrefix) + PullPackOpeningMissionUserPrefsSnapshot("post", failedDir, uniquePrefix) + PullPackOpeningResultLog(failedDir, uniquePrefix) + LogWarn("Instance: " . session.get("scriptName") . " | " . contextName . " card recognition failed; diagnostics saved to Logs\failed with prefix " . uniquePrefix) + } + GoToMain() +} + +ClaimVisibleEventRewards(eventResult) { + global session + + claimUiEvents := session.get("specialMissionClaimUiEvents") + hasClaimFilter := IsObject(claimUiEvents) + if (hasClaimFilter) { + filterCount := 0 + for k, v in claimUiEvents + filterCount++ + if (filterCount < 1) + hasClaimFilter := false + } + + for specialEventName, specialEventObj in session.get("specialEventList") { + if(eventResult.HasKey(specialEventName) && eventResult[specialEventName]) + continue + if(specialEventObj.isExpiredSpecialEvent()){ + eventResult[specialEventName] := true + continue + } + ; Only attempt claim UI for events advanced onto a ClaimDay this run. + if (hasClaimFilter && !claimUiEvents.HasKey(specialEventName)) { + eventResult[specialEventName] := true + continue + } + + if (specialEventObj.isExistNeedleInScreen(session.get("winTitle")) = 2){ + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop{ + adbClick_wbb(175, 422) + Delay(1) + adbClick_wbb(138, 451) + Delay(1) + + ; Elite Deck events: do not wait for the usual claim-complete needle. + ; The Helper is already watching MissionUserPrefs; as soon as it writes + ; result.rc the claim has been processed and we can proceed to the + ; restart + card registration in FinishEliteDeckClaim. + if (specialEventObj.isEliteDeck) { + if (HelperHasCardResult()) { + eventResult[specialEventName] := true + return specialEventName + } + } + if (FindOrLoseImage("Mission_CompleteGotAllClaims", 0, failSafeTime, , true)) { + eventResult[specialEventName] := true + return specialEventName + } + + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Get reward event: " . specialEventName . "`n(" . failSafeTime . "/45 seconds)") + if (failSafeTime > 45) { + ; For Elite Deck, do not mark the event as claimed until the + ; Helper actually reports a card result. Returning false lets + ; GetEventRewards keep retrying or scrolling instead of giving up. + if (!specialEventObj.isEliteDeck) + eventResult[specialEventName] := true + return false + } + } + } + } + + return false +} + +; Failsafe if Missions page lands on 'Deck' mission tutorial. +HandleMissionDeckFailsafe() { + Sleep, 500 + adbInput("111") ; ESC + Sleep, 500 + adbInput("111") ; ESC + Sleep, 500 + adbInput("111") ; ESC + Sleep, 500 + adbInput("111") ; ESC + Sleep, 500 + adbClick(146,438) + Sleep, 1500 + adbInput("111") ; ESC to home screen + Sleep, 1000 + return true +} diff --git a/Scripts/Include/BotTutorial.ahk b/Scripts/Include/BotTutorial.ahk new file mode 100644 index 00000000..7c7fccd2 --- /dev/null +++ b/Scripts/Include/BotTutorial.ahk @@ -0,0 +1,560 @@ +; New account tutorial and start-up popups + +DoTutorial() { + prof := Prof_Scope(A_ThisFunc) + global botConfig, session + + session.set("creationDate", A_Now) + + FindImageAndClick("Create_CountryComboBoxButton", 143, 370) ;select month and year and click + + Delay(3) + adbClick_wbb(80, 400) + Delay(3) + + tutorialLanguage := Trim(session.get("language")) + if (tutorialLanguage = "") { + ; New accounts have no metadata yet, so read the active game's language + if (EnsurePTCGPBHelperInstalled()) { + tutorialLanguage := adbWriteRaw("/data/ptcgp/ptcgpb lang", true) + tutorialLanguage := Trim(StrReplace(tutorialLanguage, "`r"), "`n`t ") + if (RegExMatch(tutorialLanguage, "i)^[a-z][a-z0-9_-]{0,15}$")) + session.set("language", tutorialLanguage) + else + tutorialLanguage := "" + } + } + StringLower, tutorialLanguage, tutorialLanguage + isYearInFront := (tutorialLanguage = "ja" || tutorialLanguage = "ko" || tutorialLanguage = "zh") + if (isYearInFront) { + ; JP/KR display the month picker in the opposite order. + Random, monthScrollCount, 4, 7 + Loop, %monthScrollCount% { + adbSwipe_wbb("100 707 100 903 150") + Delay(3) + } + } + + adbClick_wbb(80, 375) + Delay(3) + session.set("failSafe", A_TickCount) + failSafeTime := 0 + + Loop { + Delay(3) + if(FindImageAndClick("Create_SelectedYear", , , , , 1, failSafeTime)) + break + Delay(3) + adbClick_wbb(142, 159) + Delay(3) + adbClick_wbb(80, 400) + Delay(3) + adbClick_wbb(80, 375) + Delay(3) + adbClick_wbb(82, 422) + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting for Year`n(" . failSafeTime . "/45 seconds)") + } ;select month and year and click + + adbClick_wbb(200, 400) + Delay(3) + + if (!isYearInFront) { + ; Other languages display the year picker in the opposite order. + Random, yearScrollCount, 4, 7 + Loop, %yearScrollCount% { + adbSwipe_wbb("389 707 389 903 150") + Delay(3) + } + } + adbClick_wbb(200, 375) + Delay(3) + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { ;select month and year and click + Delay(3) + if(FindImageAndClick("Create_SelectedMonth", , , , , 1, failSafeTime)) + break + Delay(3) + adbClick_wbb(142, 159) + Delay(3) + adbClick_wbb(142, 159) + Delay(3) + adbClick_wbb(200, 400) + Delay(3) + adbClick_wbb(200, 375) + Delay(3) + adbClick_wbb(142, 159) + Delay(3) + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting for Month`n(" . failSafeTime . "/45 seconds)") + } ;select month and year and click + + Delay(3) + FindImageAndClick("Create_BirthConfirmCancelButton", 140, 474, , 1000) + + ;wait date confirmation screen while clicking ok + + FindImageAndClick("Create_TosOpenButton", 203, 371, , 1000) ;wait to be at the tos screen while confirming birth + + FindImageAndClick("Create_TosCloseButton", 139, 299, , 1000) ;wait for tos while clicking it + + FindImageAndClick("Create_TosOpenButton", 142, 486, , 1000) ;wait to be at the tos screen and click x + + FindImageAndClick("Common_PopupXButtonInMain", 142, 339, , 1000) ;wait to be at the tos screen + + FindImageAndClick("Create_TosOpenButton", 142, 486, , 1000) ;wait to be at the tos screen, click X + + Delay(3) + adbClick_wbb(261, 374) + + Delay(3) + adbClick_wbb(261, 406) + + Delay(3) + adbClick_wbb(145, 484) + + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + if(FindOrLoseImage("Create_SignupNotification", 0,failSafeTime)) { + ; notification flow + adbClick(138, 434) + Sleep, 1000 + adbClick(136, 350) + Sleep, 1000 + adbClick(136, 350) + Delay(1) + } + if(FindOrLoseImage("Create_SignupsDataSharing", 0, failSafeTime)) { + adbClick(82, 287) + Sleep, 1000 + adbClick(80, 355) + Sleep, 100 + adbClick(90, 425) + Sleep, 100 + adbClick(141, 484) + Delay(1) + } + if(FindImageAndClick("Create_BeginNewAccountButton", 145, 484, , , 2, failSafeTime)) ;wait to be at create save data screen while clicking + break + Delay(1) + adbClick_wbb(261, 406) + if(FindImageAndClick("Create_BeginNewAccountButton", 145, 484, , , 2, failSafeTime)) ;wait to be at create save data screen while clicking + break + Delay(1) + adbClick_wbb(261, 374) + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting for Save`n(" . failSafeTime . "/45 seconds)") + } + + Delay(1) + + adbClick_wbb(143, 348) + + Delay(1) + + failSafeTime := 0 + match := "" + Loop { + if(FindOrLoseImage("Create_NintendoLink", 0, failSafeTime)) { + match :="Create_NintendoLink" + break + } + if(FindOrLoseImage("Create_NintendoLink2", 0, failSafeTime)) { + match :="Create_NintendoLink2" + break + } + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting for Nintendo Link`n(" . failSafeTime . "/45 seconds)") + } + Delay(1) + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + if(FindOrLoseImage(match, 0, failSafeTime)){ + adbClick_wbb(140, 460) + Loop { + Delay(1) + if(FindOrLoseImage(match, 1, failSafeTime)){ + adbClick_wbb(140, 380) ; click ok on the interrupted while opening pack prompt + break + } + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + } + } else if(FindOrLoseImage("Create_DownloadAlertWindow", 0, failSafeTime)){ + adbClick_wbb(203, 364) + } else if(FindOrLoseImage("Create_DownloadComplete", 0, failSafeTime)){ + adbClick_wbb(140, 370) + } else if(FindOrLoseImage("Create_CinematicBackground", 0, failSafeTime)){ + break + } + Delay(1) + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + } + + if(session.get("setSpeed") = 3){ + FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) + FindImageAndClick(GetSpeedModNeedle(1), GetSpeedModClickX(1), GetSpeedModClickY(1)) + Delay(1) + adbClick_wbb(51, 297) + Delay(1) + } + + FindImageAndClick("Create_WelcomePopup", 253, 506, , 110) ;click through cutscene until welcome page + + if(session.get("setSpeed") = 3){ + FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) + FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) + Delay(1) + adbClick_wbb(51, 297) + Delay(1) + } + FindImageAndClick("Create_NameInputIcon", 189, 438) ;wait for name input screen + FindImageAndClick("Create_DeactivatedOKButton", 139, 257) ;wait for name input screen + + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + if (botConfig.get("AccountName") != "ERROR" && botConfig.get("AccountName") != "") { + Random, randomNum, 1, 500 ; Generate random number from 1 to 500 + username := botConfig.get("AccountName") . "-" . randomNum + username := SubStr(username, 1, 14) ; max character limit + LogDebug("Using AccountName: " . username) + } else { + fileName := A_ScriptDir . "\..\usernames.txt" + if(FileExist(fileName)) + name := ReadFile("usernames") + else + name := ReadFile("usernames_default") + + Random, randomIndex, 1, name.MaxIndex() + username := name[randomIndex] + username := SubStr(username, 1, 14) ; max character limit + LogDebug("Using random username: " . username) + } + + adbInput(username) + Delay(1) + if(FindImageAndClick("Create_PackReturnButtonIcon", 185, 372, , , 10)) + break + adbClick_wbb(90, 370) + Delay(1) + adbClick_wbb(139, 254) ; 139 254 194 372 + Delay(1) + adbClick_wbb(139, 254) + Delay(1) + EraseInput() ; incase the random pokemon is not accepted + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("In failsafe for Trace. " . failSafeTime . "/45 seconds") + if(failSafeTime > 45) + restartGameInstance("Stuck at name") + } + + Delay(1) + + adbClick_wbb(140, 424) + + FindImageAndClick("Pack_ReadyForOpenPack", 140, 424) ;wait for pack to be ready to trace + if(session.get("setSpeed") > 1) { + FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) + FindImageAndClick(GetSpeedModNeedle(1), GetSpeedModClickX(1), GetSpeedModClickY(1)) + Delay(1) + adbClick_wbb(51, 297) + Delay(1) + } + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + adbSwipe_wbb(adbSwipeParams) + Sleep, 100 + if(FindOrLoseImage("Pack_ReadyForOpenPack", 1, failSafeTime)){ + if(session.get("setSpeed") > 1) { + if(session.get("setSpeed") = 3) { + FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) + FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) ; click 3x + } + } + adbClick_wbb(51, 297) + break + } + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting for Pack`n(" . failSafeTime . "/45 seconds)") + } + + FindImageAndClick("Create_SwipeForRegisterDexIcon", 140, 375) ;click through cards until needing to swipe up + if(session.get("setSpeed") > 1) { + FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) + FindImageAndClick(GetSpeedModNeedle(1), GetSpeedModClickX(1), GetSpeedModClickY(1)) + Delay(1) + adbClick_wbb(51, 297) + Delay(1) + } + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + adbSwipe_wbb("266 770 266 355 60") + Sleep, 100 + if(FindOrLoseImage("Create_ConfirmRegisteredCard", 0, failSafeTime)){ + if(session.get("setSpeed") > 1) { + FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) + if(session.get("setSpeed") = 3) + FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) + else + FindImageAndClick(GetSpeedModNeedle(2), GetSpeedModClickX(2), GetSpeedModClickY(2)) + } + adbClick_wbb(51, 297) + break + } + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting for swipe up for " . failSafeTime . "/45 seconds") + Delay(1) + } + + Delay(1) + adbClick_wbb(204, 371) + + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + adbClick_wbb(137, 365) + Delay(1) + adbClick_wbb(137, 480) + Delay(1) + if(FindOrLoseImage("Create_MustClickMissionBackground", 0, failSafeTime)){ + break + } else if(FindOrLoseImage("Create_DownloadAlertWindow", 0, failSafeTime)){ + adbClick_wbb(203, 364) + } + } + + Delay(1) + adbClick_wbb(247, 472) + + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + if(FindOrLoseImage("Create_TutorialPackOpenNotifyIcon", 0, failSafeTime)) { + break + } + adbClick_wbb(90, 260) + adbClick_wbb(140, 400) + adbClick_wbb(137, 340) + Delay(3) + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting for pack notification " . failSafeTime . "/45 seconds") + } + + FindImageAndClick("Create_TutorialPackOpenNotifyIcon", 145, 194) ;click on packs. stop at booster pack tutorial + + Delay(3) + adbClick_wbb(142, 436) + Delay(3) + adbClick_wbb(142, 436) + Delay(3) + adbClick_wbb(142, 436) + Delay(3) + adbClick_wbb(142, 436) + + FindImageAndClick("Pack_ReadyForOpenPack", 239, 497) ;wait for pack to be ready to Trace + if(session.get("setSpeed") > 1) { + FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) + FindImageAndClick(GetSpeedModNeedle(1), GetSpeedModClickX(1), GetSpeedModClickY(1)) + Delay(1) + adbClick_wbb(51, 297) + Delay(1) + } + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + adbSwipe_wbb(adbSwipeParams) + Sleep, 100 + if(FindOrLoseImage("Pack_ReadyForOpenPack", 1, failSafeTime)){ + if(session.get("setSpeed") > 1) { + if(session.get("setSpeed") = 3) { + FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) + FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) + } + } + adbClick_wbb(51, 297) + break + } + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting for Pack`n(" . failSafeTime . "/45 seconds)") + Delay(1) + } + + FindImageAndClick("Pack_ResultAfterOpenPack", 252, 505, 5, 50) ;skip through cards until results opening screen + + FindImageAndClick("Pack_SkipButtonAfterOpenPack", 146, 496) ;click on next until skip button appears + + FindImageAndClick("Pack_NextButtonAfterOpenPack", 239, 497, , , 2) + + FindImageAndClick("Create_UnlockedWonerPickIconInLevelUp", 146, 494) ;click on next until skip button appearsstop at hourglasses tutorial + + Delay(3) + + adbClick_wbb(140, 358) + + FindImageAndClick("Common_ShopButtonInMain", 146, 444) ;click until at main menu + + ; New needle & search region 11.1.2025 kevinnnn + FindImageAndClick("Create_CardImageInTutorialWPFirstScreen", 79, 411) + + FindImageAndClick("Create_WPItemBottomBorder", 190, 437) ; click through tutorial + + Delay(2) + + adbClick_wbb(202, 347) ; select Wonder Pick item to open Bonus Pick confirm + Delay(2) + + ; Bonus Pick confirm (No cost / OK). Wonder4 selection needle often never matches on current UI. + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + adbClick_wbb(208, 461) ; OK + Delay(1) + if(FindOrLoseImage("Create_TitleBottomBorderInWPSelectCard", 0, failSafeTime)) + break + ; Dialog may not have opened yet — retry select + adbClick_wbb(202, 347) + Delay(1) + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Confirming WonderPick`n(" . failSafeTime . "/45 seconds)") + } + + if(session.get("setSpeed") = 3) ;time the animation + Sleep, 1500 + else + Sleep, 2500 + + Delay(1) + + adbClick_wbb(187, 345) + + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + if(session.get("setSpeed") = 3) + continueTime := 1 + else + continueTime := 3 + + if (TryWonderPickDexSwipeRegister()) { + failSafeTime := 0 + } else if(FindOrLoseImage("Pack_SkipButtonAfterOpenPack", 0, failSafeTime)) { + adbClick_wbb(239, 497) + } else if(FindOrLoseImage("Create_WelcomePopup", 0, failSafeTime)) { ;click through to end of tut screen + break + } else if(FindOrLoseImage("Pack_NextButtonAfterOpenPack", 0, failSafeTime)) { + adbClick_wbb(146, 494) ;146, 494 + } else if(FindOrLoseImage("Next2", 0, failSafeTime)) { + adbClick_wbb(146, 494) ;146, 494 + } else { + adbClick_wbb(187, 345) + Delay(1) + adbClick_wbb(143, 492) + Delay(1) + adbClick_wbb(143, 492) + Delay(1) + } + Delay(1) + + ; adbClick_wbb(66, 446) + ; Delay(1) + ; adbClick_wbb(66, 446) + ; Delay(1) + ; adbClick_wbb(66, 446) + ; Delay(1) + ; adbClick_wbb(187, 345) + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting for End`n(" . failSafeTime . "/45 seconds)") + } + + FindImageAndClick("Create_FullFreepackInMainCenter", 192, 449) ;click until at main menu + EnterGameFromWelcomeIfNeeded() ; 1.7.0 forces restart after WP onto Welcome before GoToMain + + return true +} + +DismissMainCloseAlertWindow(context := "") { + if(!FindOrLoseImage("Common_CloseAlertWindowInMain", 0, , , true)) + return false + + LogInfo("Dismissed close-alert popup | context=" . context) + if(context != "") + CreateStatusMessage("Closing app quit confirmation`n" . context,,,, false) + else + CreateStatusMessage("Closing app quit confirmation",,,, false) + + adbClick_wbb(75, 365) + Delay(1) + return true +} + +; After create-tutorial WP, 1.7.0 always forces an app restart onto Welcome (Tap to Start). +; ESC there opens quit confirmation — tap to enter instead. +; Returns true once past title screen (home/shop/news X/etc.). +EnterGameFromWelcomeIfNeeded(timeoutSec := 60) { + global session + + session.set("failSafe", A_TickCount) + Loop { + ; Already in-game (including News/X overlays) — let GoToMain use ESC. + if (FindOrLoseImage("Common_ShopButtonInMain", 0, 0, , true) + || FindOrLoseImage("Common_ActivatedHomeInMainMenu", 0, 0, , true) + || FindOrLoseImage("Pack_PackPointButton", 0, 0, , true) + || FindOrLoseImage("Create_FullFreepackInMainCenter", 0, 0, , true) + || FindOrLoseImage("WonderPick_WonderPickButtonInHome", 0, 0, , true) + || FindOrLoseImage("Common_PopupXButtonInMain", 0, 0, , true)) + return true + + if (DismissMainCloseAlertWindow("Welcome gate")) { + Delay(1) + adbClick_wbb(140, 450) ; Tap to Start under quit dialog + Delay(2) + CreateStatusMessage("Entering game from Welcome...") + continue + } + + if (FindOrLoseImage("Create_WelcomePopup", 0, 0, , true) + || FindOrLoseImage("Boot_Welcome", 0, 0, , true) + || FindOrLoseImage("Boot_Welcome2", 0, 0, , true)) { + adbClick_wbb(140, 450) ; Tap to Start + Delay(2) + CreateStatusMessage("Entering game from Welcome...") + continue + } + + Delay(1) + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting past Welcome`n(" . failSafeTime . "/" . timeoutSec . " seconds)") + if (failSafeTime >= timeoutSec) + return false + } +} + +TryDismisMyCardsTutorial(failSafeTime) { + if (!FindOrLoseImage("Common_BlurryMyCards", 0, , , true)) { + return false + } + + adbClick(168, 433) + Delay(1) + adbClick(168, 433) + Delay(1) + adbClick(168, 433) + Delay(10) + + adbClick(168, 471) + Delay(1) + adbClick(168, 471) + Delay(1) + adbClick(168, 471) + Delay(1) + adbClick(168, 471) + Delay(10) + + adbClick(168, 432) + Delay(10) +} diff --git a/Scripts/Include/BotWonderPick.ahk b/Scripts/Include/BotWonderPick.ahk new file mode 100644 index 00000000..0384a2d0 --- /dev/null +++ b/Scripts/Include/BotWonderPick.ahk @@ -0,0 +1,190 @@ +; Wonder pick flow + +; Wonder Pick reveals can show the first-time card-dex register tutorial. +; Skip dismisses it; no swipe needed. +TryWonderPickDexSwipeRegister() { + global session + + if (!FindOrLoseImage("Create_SwipeForRegisterDexIcon", 0, 0, 20, 1)) + return false + + CreateStatusMessage("WonderPick: skip card dex tutorial...") + adbClick_wbb(239, 497) + Delay(1) + session.set("failSafe", A_TickCount) + return true +} + +DoWonderPickOnly() { + global session + + session.set("failSafe", A_TickCount) + failSafeTime := 0 + pickAlreadyRevealed := false + + if (isTerminatePTCGPHelperApp()) { + InitPackOpening() + } + Loop { + if (TryWonderPickDexSwipeRegister()) { + pickAlreadyRevealed := true + break + } + adbClick_wbb(80, 390) ; first wonderpick slot + adbClick_wbb(80, 460) ; backup, second wonderpick slot + if(FindOrLoseImage("WonderPick_NoEnergy", 0, failSafeTime)) { + Sleep, 2000 + CreateStatusMessage("No WonderPick Energy left!",,,, false) + Sleep, 2000 + adbClick_wbb(137, 505) + Sleep, 2000 + adbClick_wbb(35, 515) + Sleep, 4000 + SetWonderPickMetadataFlag() + return + } + if(FindOrLoseImage("WonderPick_WonderPickButtonInHome", 1, failSafeTime)) { + if(FindOrLoseImage("WonderPick_EnergyStatusAfterSelect", 0, failSafeTime)){ + adbClick_wbb(198, 456) + Delay(3) + } + if(FindOrLoseImage("WonderPick_SelectCards", 0, failSafeTime)) + break + } + Delay(1) + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting for WonderPick`n(" . failSafeTime . "/45 seconds)") + } + if (!pickAlreadyRevealed) { + Sleep, 300 + if(botConfig.get("slowMotion")) + Sleep, 3000 + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + if (TryWonderPickDexSwipeRegister()) { + pickAlreadyRevealed := true + break + } + adbClick_wbb(183, 350) ; click card + if(FindOrLoseImage("WonderPick_SelectCards", 1, failSafeTime)) { + break + } + Delay(1) + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting for Card`n(" . failSafeTime . "/45 seconds)") + } + } + session.set("failSafe", A_TickCount) + failSafeTime := 0 + ;TODO thanks and wonder pick 5 times for missions + Loop { + if (TryWonderPickDexSwipeRegister()) { + failSafeTime := 0 + continue + } + adbClick_wbb(146, 494) + Delay(1) + if(FindOrLoseImage("Pack_SkipButtonAfterOpenPack", 0, failSafeTime) || FindOrLoseImage("WonderPick_WonderPickButtonInHome", 0, failSafeTime)) + break + if(FindOrLoseImage("WonderPick_SelectCards", 0, failSafeTime)) { + adbClick_wbb(183, 350) ; click card + } + Delay(1) + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting for Shop`n(" . failSafeTime . "/45 seconds)") + } + + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + Delay(1) + if (TryWonderPickDexSwipeRegister()) { + failSafeTime := 0 + continue + } + if(FindOrLoseImage("Common_ShopButtonInMain", 0, failSafeTime)) + break + else if(FindOrLoseImage("Pack_SkipButtonAfterOpenPack", 0, failSafeTime)) + adbClick_wbb(239, 497) + else if(FindOrLoseImage("WonderPick_SelectCards", 0, failSafeTime)) { + adbClick_wbb(183, 350) ; click card + } + else if(FindOrLoseImage("Common_PopupXButtonInMain", 0, , , true)){ + adbClick_wbb(137, 480) + Delay(1) + } + else + adbInputEvent("111") ;send ESC + Delay(4) + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting for Shop`n(" . failSafeTime . "/45 seconds)") + } + result := EvaluatePack() + if (result) { + LogToCardDatabase(result) + } + SetWonderPickMetadataFlag() +} + +DoWonderPick() { + global session + + FindImageAndClick("Common_ShopButtonInMain", 40, 515) ;click until at main menu + + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + if(FindOrLoseImage("WonderPick_WonderPickButtonInHome", 0, failSafeTime)) + break + else if(FindOrLoseImage("Common_PopupXButtonInMain", 0, , , true)){ + adbClick_wbb(137, 480) + } + else + adbClick_wbb(59, 429) + Delay(1) + } + + DoWonderPickOnly() + + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + adbClick(261, 478) + Sleep, 1000 + if FindOrLoseImage("Mission_ActivatedBeginnerMissionTabButton", 0, failSafeTime) + break + else if FindOrLoseImage("Mission_GoToDexButtonIcon", 0, failSafeTime) + break + else if FindOrLoseImage("Mission_DailyMissionImage", 0, failSafeTime) + break + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + } + + ;FindImageAndClick("WPMission", 150, 286, , 1000) + FindImageAndClick("Mission_FirstWonderpickMissionIconInDetails", 150, 286, , 1000) + session.set("failSafe", A_TickCount) + failSafeTime := 0 + Loop { + Delay(1) + adbClick_wbb(139, 424) + Delay(1) + clickButton := FindOrLoseImage("Common_ColorChangeButton", 0, failSafeTime, 80) + if(clickButton) { + adbClick_wbb(110, 369) + } + else if(FindOrLoseImage("Common_ShopButtonInMain", 1, failSafeTime)){ + GoToMain() + break + } + else if(FindOrLoseImage("Common_PopupXButtonInMain", 0, , , true)){ + adbClick_wbb(137, 480) + Delay(1) + } + else + break + failSafeTime := (A_TickCount - session.get("failSafe")) // 1000 + CreateStatusMessage("Waiting for WonderPick`n(" . failSafeTime . "/45 seconds)") + } + return true +} From 7e9675c7360e90c5c46ad8e64a54b17147fd7a01 Mon Sep 17 00:00:00 2001 From: Lean Date: Mon, 12 Oct 2026 19:27:58 +0200 Subject: [PATCH 06/10] Fix overlay --- Scripts/1.ahk | 1 + Scripts/Include/ADB.ahk | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/Scripts/1.ahk b/Scripts/1.ahk index cbd19ae4..0f2369a5 100644 --- a/Scripts/1.ahk +++ b/Scripts/1.ahk @@ -215,6 +215,7 @@ if(InStr(botConfig.get("deleteMethod"), "Inject") || botConfig.get("deleteMethod initializeAdbShell() RemoveOldFiles() +adbGrantOverlayPermission() if(session.get("injectMethod")) createAccountList(session.get("scriptName")) diff --git a/Scripts/Include/ADB.ahk b/Scripts/Include/ADB.ahk index 327320aa..55471cf1 100644 --- a/Scripts/Include/ADB.ahk +++ b/Scripts/Include/ADB.ahk @@ -519,6 +519,24 @@ closePTCGPApp(){ DelayH(100) } +; Allows the game (Mod Menu apk) to draw over other apps, like the Instance Manager does +; after installing the apk. Returns true if the permission is enabled afterwards. +adbGrantOverlayPermission(package := "jp.pokemon.pokemontcgp") { + prof := Prof_Scope(A_ThisFunc) + check := "cmd appops get " . package . " SYSTEM_ALERT_WINDOW" + if (InStr(adbWriteRaw(check, true), "SYSTEM_ALERT_WINDOW: allow")) + return true + + adbWriteRaw("cmd appops set " . package . " SYSTEM_ALERT_WINDOW allow") + output := adbWriteRaw(check, true) + if (!InStr(output, "SYSTEM_ALERT_WINDOW: allow")) { + LogWarn("Could not enable the overlay permission for " . package . ": " . Trim(output, "`r`n`t "), "ADB.txt") + return false + } + LogInfo("Enabled the overlay permission for " . package, "ADB.txt") + return true +} + isPTCGPAppInFocus() { prof := Prof_Scope(A_ThisFunc) global session From c3d0f88f1792d182245c97e1fb26a8084c9a47d6 Mon Sep 17 00:00:00 2001 From: Lean Date: Mon, 12 Oct 2026 20:08:23 +0200 Subject: [PATCH 07/10] Refactor speed mod menu clicks --- Scripts/1.ahk | 12 +---- Scripts/Include/BotAccountMenu.ahk | 6 +-- Scripts/Include/BotMissions.ahk | 6 +-- Scripts/Include/BotPackOpening.ahk | 20 ++------ Scripts/Include/BotTutorial.ahk | 33 +++++-------- Scripts/Include/Coords.ahk | 14 +++++- Scripts/Include/MonitorOnce.ahk | 75 ------------------------------ 7 files changed, 33 insertions(+), 133 deletions(-) delete mode 100644 Scripts/Include/MonitorOnce.ahk diff --git a/Scripts/1.ahk b/Scripts/1.ahk index 0f2369a5..0a291f8b 100644 --- a/Scripts/1.ahk +++ b/Scripts/1.ahk @@ -247,11 +247,7 @@ if(DeadCheck = 1 && botConfig.get("deleteMethod") != "Create Bots (13P)") { session.set("friended", true) CreateStatusMessage("Stuck account still has friends. Unfriending accounts...") waitForAppBootScreen() - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - if(session.get("setSpeed") = 3) - FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) - else - FindImageAndClick(GetSpeedModNeedle(2), GetSpeedModClickX(2), GetSpeedModClickY(2)) + SetConfiguredGameSpeed() adbClick_wbb(51, 297) Delay(1) startPreProcess(botConfig.get("deleteMethod")) @@ -411,11 +407,7 @@ if(DeadCheck = 1 && botConfig.get("deleteMethod") != "Create Bots (13P)") { } waitForAppBootScreen() - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - if(session.get("setSpeed") = 3) - FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) - else - FindImageAndClick(GetSpeedModNeedle(2), GetSpeedModClickX(2), GetSpeedModClickY(2)) + SetConfiguredGameSpeed() Delay(1) adbClick_wbb(51, 297) Delay(1) diff --git a/Scripts/Include/BotAccountMenu.ahk b/Scripts/Include/BotAccountMenu.ahk index 37400f1b..6b58619f 100644 --- a/Scripts/Include/BotAccountMenu.ahk +++ b/Scripts/Include/BotAccountMenu.ahk @@ -88,11 +88,7 @@ menuDeleteStart() { return session.get("keepAccount") } if(session.get("friended")) { - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - if(session.get("setSpeed") = 3) - FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) - else - FindImageAndClick(GetSpeedModNeedle(2), GetSpeedModClickX(2), GetSpeedModClickY(2)) + SetConfiguredGameSpeed() Delay(1) adbClick_wbb(51, 297) Delay(1) diff --git a/Scripts/Include/BotMissions.ahk b/Scripts/Include/BotMissions.ahk index 724c74d3..4c7e7feb 100644 --- a/Scripts/Include/BotMissions.ahk +++ b/Scripts/Include/BotMissions.ahk @@ -400,11 +400,7 @@ FinishEliteDeckClaim(helperResult := false, contextName := "Elite Deck", failedP ; loading an injected account (SpeedMod menu), then run the normal ; startPreProcess so the game reaches the expected main screen. waitForAppBootScreen() - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - if(session.get("setSpeed") = 3) - FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) - else - FindImageAndClick(GetSpeedModNeedle(2), GetSpeedModClickX(2), GetSpeedModClickY(2)) + SetConfiguredGameSpeed() Delay(1) adbClick_wbb(51, 297) Delay(1) diff --git a/Scripts/Include/BotPackOpening.ahk b/Scripts/Include/BotPackOpening.ahk index 8192bdf8..cfc3aa29 100644 --- a/Scripts/Include/BotPackOpening.ahk +++ b/Scripts/Include/BotPackOpening.ahk @@ -519,8 +519,7 @@ PackOpening(tenPackOpening := false) { } if(session.get("setSpeed") > 1) { - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - FindImageAndClick(GetSpeedModNeedle(1), GetSpeedModClickX(1), GetSpeedModClickY(1)) + SetGameSpeed(1) Delay(1) adbClick_wbb(51, 297) Delay(1) @@ -533,8 +532,7 @@ PackOpening(tenPackOpening := false) { if (FindOrLoseImage("Pack_ReadyForOpenPack", 1, failSafeTime)){ if(session.get("setSpeed") > 1) { if(session.get("setSpeed") = 3) { - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) + SetGameSpeed(3) } } adbClick_wbb(51, 292) @@ -728,8 +726,7 @@ HourglassOpening(HG := false, NEIRestart := true, tenPackOpening := false) { } if(session.get("setSpeed") > 1) { - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - FindImageAndClick(GetSpeedModNeedle(1), GetSpeedModClickX(1), GetSpeedModClickY(1)) + SetGameSpeed(1) Delay(1) adbClick_wbb(51, 297) Delay(1) @@ -742,8 +739,7 @@ HourglassOpening(HG := false, NEIRestart := true, tenPackOpening := false) { if (FindOrLoseImage("Pack_ReadyForOpenPack", 1, failSafeTime)){ if(session.get("setSpeed") > 1) { if(session.get("setSpeed") = 3) { - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) + SetGameSpeed(3) } } adbClick_wbb(51, 297) @@ -936,13 +932,7 @@ SetOpenGiftSpeed(targetSpeed) { } SetOpenGiftSpeedByButtons(targetSpeed) { - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - if(targetSpeed = 1) - FindImageAndClick(GetSpeedModNeedle(1), GetSpeedModClickX(1), GetSpeedModClickY(1)) - else if(targetSpeed = 3) - FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) - else - FindImageAndClick(GetSpeedModNeedle(2), GetSpeedModClickX(2), GetSpeedModClickY(2)) + SetGameSpeed(targetSpeed = 1 || targetSpeed = 3 ? targetSpeed : 2) Delay(1) adbClick_wbb(51, 297) diff --git a/Scripts/Include/BotTutorial.ahk b/Scripts/Include/BotTutorial.ahk index 7c7fccd2..e6b7cd09 100644 --- a/Scripts/Include/BotTutorial.ahk +++ b/Scripts/Include/BotTutorial.ahk @@ -194,8 +194,7 @@ DoTutorial() { } if(session.get("setSpeed") = 3){ - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - FindImageAndClick(GetSpeedModNeedle(1), GetSpeedModClickX(1), GetSpeedModClickY(1)) + SetGameSpeed(1) Delay(1) adbClick_wbb(51, 297) Delay(1) @@ -204,8 +203,7 @@ DoTutorial() { FindImageAndClick("Create_WelcomePopup", 253, 506, , 110) ;click through cutscene until welcome page if(session.get("setSpeed") = 3){ - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) + SetGameSpeed(3) Delay(1) adbClick_wbb(51, 297) Delay(1) @@ -257,8 +255,7 @@ DoTutorial() { FindImageAndClick("Pack_ReadyForOpenPack", 140, 424) ;wait for pack to be ready to trace if(session.get("setSpeed") > 1) { - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - FindImageAndClick(GetSpeedModNeedle(1), GetSpeedModClickX(1), GetSpeedModClickY(1)) + SetGameSpeed(1) Delay(1) adbClick_wbb(51, 297) Delay(1) @@ -271,8 +268,7 @@ DoTutorial() { if(FindOrLoseImage("Pack_ReadyForOpenPack", 1, failSafeTime)){ if(session.get("setSpeed") > 1) { if(session.get("setSpeed") = 3) { - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) ; click 3x + SetGameSpeed(3) ; click 3x } } adbClick_wbb(51, 297) @@ -284,8 +280,7 @@ DoTutorial() { FindImageAndClick("Create_SwipeForRegisterDexIcon", 140, 375) ;click through cards until needing to swipe up if(session.get("setSpeed") > 1) { - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - FindImageAndClick(GetSpeedModNeedle(1), GetSpeedModClickX(1), GetSpeedModClickY(1)) + SetGameSpeed(1) Delay(1) adbClick_wbb(51, 297) Delay(1) @@ -297,11 +292,7 @@ DoTutorial() { Sleep, 100 if(FindOrLoseImage("Create_ConfirmRegisteredCard", 0, failSafeTime)){ if(session.get("setSpeed") > 1) { - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - if(session.get("setSpeed") = 3) - FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) - else - FindImageAndClick(GetSpeedModNeedle(2), GetSpeedModClickX(2), GetSpeedModClickY(2)) + SetConfiguredGameSpeed() } adbClick_wbb(51, 297) break @@ -328,12 +319,12 @@ DoTutorial() { } } - Delay(1) - adbClick_wbb(247, 472) - session.set("failSafe", A_TickCount) failSafeTime := 0 Loop { + Delay(1) + adbClick_wbb(247, 472) + Delay(1) if(FindOrLoseImage("Create_TutorialPackOpenNotifyIcon", 0, failSafeTime)) { break } @@ -358,8 +349,7 @@ DoTutorial() { FindImageAndClick("Pack_ReadyForOpenPack", 239, 497) ;wait for pack to be ready to Trace if(session.get("setSpeed") > 1) { - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - FindImageAndClick(GetSpeedModNeedle(1), GetSpeedModClickX(1), GetSpeedModClickY(1)) + SetGameSpeed(1) Delay(1) adbClick_wbb(51, 297) Delay(1) @@ -372,8 +362,7 @@ DoTutorial() { if(FindOrLoseImage("Pack_ReadyForOpenPack", 1, failSafeTime)){ if(session.get("setSpeed") > 1) { if(session.get("setSpeed") = 3) { - FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) - FindImageAndClick(GetSpeedModNeedle(3), GetSpeedModClickX(3), GetSpeedModClickY(3)) + SetGameSpeed(3) } } adbClick_wbb(51, 297) diff --git a/Scripts/Include/Coords.ahk b/Scripts/Include/Coords.ahk index c3c2e7e7..75bee4ad 100644 --- a/Scripts/Include/Coords.ahk +++ b/Scripts/Include/Coords.ahk @@ -1,4 +1,4 @@ -class Coordinate{ +class Coordinate{ startX := 0 startY := 0 endX := 0 @@ -315,6 +315,18 @@ GetSpeedModClickY(speed) { return session.get("muMuAndroid15") ? 166 : 172 } +; Opens the SpeedMod menu and selects the given speed (1, 2 or 3). +SetGameSpeed(speed) { + FindImageAndClick("Common_SpeedModMenuButton", 18, 109, , 2000) + FindImageAndClick(GetSpeedModNeedle(speed), GetSpeedModClickX(speed), GetSpeedModClickY(speed)) +} + +; Opens the SpeedMod menu and selects the configured speed (3x, otherwise 2x). +SetConfiguredGameSpeed() { + global session + SetGameSpeed(session.get("setSpeed") = 3 ? 3 : 2) +} + IsSpeedModImageName(imageName) { return (imageName = "speedmodMenu" || imageName = "One" || imageName = "Two" || imageName = "Three" diff --git a/Scripts/Include/MonitorOnce.ahk b/Scripts/Include/MonitorOnce.ahk deleted file mode 100644 index b5bdd11d..00000000 --- a/Scripts/Include/MonitorOnce.ahk +++ /dev/null @@ -1,75 +0,0 @@ -#Include %A_ScriptDir%\Config.ahk -#Include %A_ScriptDir%\Profiler.ahk -#Include %A_ScriptDir%\Logging.ahk -#Include %A_ScriptDir%\Utils.ahk -#Include %A_ScriptDir%\AhkProcess.ahk - -#SingleInstance, force -CoordMode, Mouse, Screen -SetTitleMatchMode, 3 - -if not A_IsAdmin -{ - ; Relaunch script with admin rights - Run *RunAs "%A_ScriptFullPath%" - ExitApp -} - -global botConfig := new BotConfig() -botConfig.loadSettingsToConfig("ALL") - -waitAfterBulkLaunch := botConfig.get("waitAfterBulkLaunch") -instanceLaunchDelay := botConfig.get("instanceLaunchDelay") -Instances := botConfig.get("Instances") -mumuFolder := botConfig.get("folderPath") - -; Loop through each instance, check if it's started, and start it if it's not -launched := 0 - -nowEpoch := A_NowUTC -EnvSub, nowEpoch, 1970, seconds - -Loop %Instances% { - instanceNum := Format("{:u}", A_Index) - - IniRead, LastEndEpoch, %A_ScriptDir%\..\%instanceNum%.ini, Metrics, LastEndEpoch, 0 - secondsSinceLastEnd := nowEpoch - LastEndEpoch - if(LastEndEpoch > 0 && secondsSinceLastEnd > (15 * 60)) - { - ; msgbox, Killing Instance %instanceNum%! Last Run Completed %secondsSinceLastEnd% Seconds Ago - msg := "Killing Instance " . instanceNum . "! Last Run Completed " . secondsSinceLastEnd . " Seconds Ago" - LogInfo(msg, "Monitor.txt") - - scriptName := instanceNum . ".ahk" - coverHwnd := CaptureMuMuCoverWindow(instanceNum) - StoreMuMuCoverWindow(instanceNum, coverHwnd) - - killedAHK := killAHK(scriptName) - killedInstance := killInstance(instanceNum) - Sleep, 3000 - - cntAHK := checkAHK(scriptName) - pID := checkInstance(instanceNum) - if not pID && not cntAHK { - ; Change the last end date to now so that we don't keep trying to restart this beast - IniWrite, %nowEpoch%, %A_ScriptDir%\..\%instanceNum%.ini, Metrics, LastEndEpoch - - launchInstance(instanceNum) - - sleepTime := instanceLaunchDelay * 1000 - Sleep, % sleepTime - launched := launched + 1 - - Sleep, %waitAfterBulkLaunch% - - ;Command := "Scripts\" . scriptName - ;Run, %Command% - scriptPath := A_ScriptDir "\.." "\" scriptName - Run, "%A_AhkPath%" /restart "%scriptPath%" - } - } -} - -ExitApp - -~+F7::ExitApp From 9c7cb6902513b227919c82820114216b7a2794e6 Mon Sep 17 00:00:00 2001 From: Lean Date: Mon, 12 Oct 2026 20:31:56 +0200 Subject: [PATCH 08/10] Refactor more duplicate logic --- Scripts/Include/AccountMetadata.ahk | 232 +++----------- Scripts/Include/CardDetection.ahk | 469 ++++++++-------------------- 2 files changed, 176 insertions(+), 525 deletions(-) diff --git a/Scripts/Include/AccountMetadata.ahk b/Scripts/Include/AccountMetadata.ahk index 58e3f025..5983e5f2 100644 --- a/Scripts/Include/AccountMetadata.ahk +++ b/Scripts/Include/AccountMetadata.ahk @@ -1699,65 +1699,26 @@ AccountMetadata_TryClaimForceInject(instance, fileName, filePath) { return saved } -AccountMetadata_SetShinedust(deviceAccount, shinedustValue, instance := "", fileName := "") { - if (deviceAccount = "") - return false +; Sets fields on the account of a device account, creating it if needed. The caller +; passes the values (fields). fillFromFileName names the field that is derived from the +; file name ("createdAt" or "packCount"); onlyIfUnset skips the update when that field +; already has a value. +AccountMetadata_SetDeviceFields(deviceAccount, fields, instance := "", fileName := "", fillFromFileName := "createdAt", onlyIfUnset := "") { if (AccountMetadata_UseTempWrites()) { - account := AccountMetadata_NewAccount(instance, fileName) - account["deviceAccount"] := deviceAccount - if (instance != "") - account["instance"] := instance - if (fileName != "") - account["fileName"] := fileName - account["shinedust"] := AccountMetadata_NewShinedust(shinedustValue, AccountMetadata_Now()) - return AccountMetadata_SaveTempAccount(instance, fileName, account) - } - - hMutex := AccountMetadata_AcquireLock() - if (!hMutex) - return false - store := AccountMetadata_ReadStoreUnlocked() - key := AccountMetadata_FindKey(store, instance, fileName, "", deviceAccount) - if (store["accounts"].HasKey(key)) { - account := store["accounts"][key] - } else { - account := AccountMetadata_NewAccount(instance, fileName) - account["deviceAccount"] := deviceAccount - } - account["deviceAccount"] := deviceAccount - if (instance != "") { - account["instance"] := instance - } - if (fileName != "") { - account["fileName"] := fileName - if (account["packCount"] = "") { - account["packCount"] := AccountMetadata_ExtractPackCount(fileName) + if (onlyIfUnset != "") { + existing := AccountMetadata_ReadAccountUnlocked(deviceAccount, instance, fileName) + if (existing[onlyIfUnset] != "") + return true } - } - account["shinedust"] := AccountMetadata_NewShinedust(shinedustValue, AccountMetadata_Now()) - - newKey := AccountMetadata_DeviceKey(deviceAccount) - if (key != newKey && store["accounts"].HasKey(key)) { - store["accounts"].Delete(key) - } - - store["accounts"][newKey] := account - AccountMetadata_WriteStoreUnlocked(store) - AccountMetadata_ReleaseLock(hMutex) - return true -} -AccountMetadata_SetLastPackPulledNow(deviceAccount, instance := "", fileName := "") { - if (deviceAccount = "") - return false - if (AccountMetadata_UseTempWrites()) { account := AccountMetadata_NewAccount(instance, fileName) account["deviceAccount"] := deviceAccount if (instance != "") account["instance"] := instance if (fileName != "") account["fileName"] := fileName - account["lastPackPulled"] := AccountMetadata_Now() + for field, value in fields + account[field] := value return AccountMetadata_SaveTempAccount(instance, fileName, account) } @@ -1774,16 +1735,25 @@ AccountMetadata_SetLastPackPulledNow(deviceAccount, instance := "", fileName := account["deviceAccount"] := deviceAccount } + if (onlyIfUnset != "" && account[onlyIfUnset] != "") { + AccountMetadata_ReleaseLock(hMutex) + return true + } + account["deviceAccount"] := deviceAccount if (instance != "") account["instance"] := instance if (fileName != "") { account["fileName"] := fileName - if (account["createdAt"] = "" || account["createdAt"] = "0") + if (fillFromFileName = "packCount") { + if (account["packCount"] = "") + account["packCount"] := AccountMetadata_ExtractPackCount(fileName) + } else if (account["createdAt"] = "" || account["createdAt"] = "0") { account["createdAt"] := AccountMetadata_ExtractCreatedAt(fileName) + } } - - account["lastPackPulled"] := AccountMetadata_Now() + for field, value in fields + account[field] := value newKey := AccountMetadata_DeviceKey(deviceAccount) if (key != newKey && store["accounts"].HasKey(key)) @@ -1794,162 +1764,36 @@ AccountMetadata_SetLastPackPulledNow(deviceAccount, instance := "", fileName := return true } -AccountMetadata_SetLastLoggedInNow(deviceAccount, instance := "", fileName := "") { +AccountMetadata_SetShinedust(deviceAccount, shinedustValue, instance := "", fileName := "") { if (deviceAccount = "") return false - if (AccountMetadata_UseTempWrites()) { - account := AccountMetadata_NewAccount(instance, fileName) - account["deviceAccount"] := deviceAccount - if (instance != "") - account["instance"] := instance - if (fileName != "") - account["fileName"] := fileName - account["lastLoggedIn"] := AccountMetadata_Now() - return AccountMetadata_SaveTempAccount(instance, fileName, account) - } + return AccountMetadata_SetDeviceFields(deviceAccount, {"shinedust": AccountMetadata_NewShinedust(shinedustValue, AccountMetadata_Now())}, instance, fileName, "packCount") +} - hMutex := AccountMetadata_AcquireLock() - if (!hMutex) +AccountMetadata_SetLastPackPulledNow(deviceAccount, instance := "", fileName := "") { + if (deviceAccount = "") return false - - store := AccountMetadata_ReadStoreUnlocked() - key := AccountMetadata_FindKey(store, instance, fileName, "", deviceAccount) - if (store["accounts"].HasKey(key)) { - account := store["accounts"][key] - } else { - account := AccountMetadata_NewAccount(instance, fileName) - account["deviceAccount"] := deviceAccount - } - - account["deviceAccount"] := deviceAccount - if (instance != "") - account["instance"] := instance - if (fileName != "") { - account["fileName"] := fileName - if (account["createdAt"] = "" || account["createdAt"] = "0") - account["createdAt"] := AccountMetadata_ExtractCreatedAt(fileName) - } - - account["lastLoggedIn"] := AccountMetadata_Now() - - newKey := AccountMetadata_DeviceKey(deviceAccount) - if (key != newKey && store["accounts"].HasKey(key)) - store["accounts"].Delete(key) - store["accounts"][newKey] := account - AccountMetadata_WriteStoreUnlocked(store) - AccountMetadata_ReleaseLock(hMutex) - return true + return AccountMetadata_SetDeviceFields(deviceAccount, {"lastPackPulled": AccountMetadata_Now()}, instance, fileName) } -AccountMetadata_SetLanguageIfUnset(deviceAccount, language, instance := "", fileName := "") { - if (deviceAccount = "" || language = "") +AccountMetadata_SetLastLoggedInNow(deviceAccount, instance := "", fileName := "") { + if (deviceAccount = "") return false + return AccountMetadata_SetDeviceFields(deviceAccount, {"lastLoggedIn": AccountMetadata_Now()}, instance, fileName) +} +AccountMetadata_SetLanguageIfUnset(deviceAccount, language, instance := "", fileName := "") { language := Trim(language) - if (language = "") - return false - - if (AccountMetadata_UseTempWrites()) { - existing := AccountMetadata_ReadAccountUnlocked(deviceAccount, instance, fileName) - if (existing["language"] != "") - return true - - account := AccountMetadata_NewAccount(instance, fileName) - account["deviceAccount"] := deviceAccount - if (instance != "") - account["instance"] := instance - if (fileName != "") - account["fileName"] := fileName - account["language"] := language - return AccountMetadata_SaveTempAccount(instance, fileName, account) - } - - hMutex := AccountMetadata_AcquireLock() - if (!hMutex) + if (deviceAccount = "" || language = "") return false - - store := AccountMetadata_ReadStoreUnlocked() - key := AccountMetadata_FindKey(store, instance, fileName, "", deviceAccount) - if (store["accounts"].HasKey(key)) { - account := store["accounts"][key] - } else { - account := AccountMetadata_NewAccount(instance, fileName) - account["deviceAccount"] := deviceAccount - } - - if (account["language"] != "") { - AccountMetadata_ReleaseLock(hMutex) - return true - } - - account["deviceAccount"] := deviceAccount - if (instance != "") - account["instance"] := instance - if (fileName != "") { - account["fileName"] := fileName - if (account["createdAt"] = "" || account["createdAt"] = "0") - account["createdAt"] := AccountMetadata_ExtractCreatedAt(fileName) - } - account["language"] := language - - newKey := AccountMetadata_DeviceKey(deviceAccount) - if (key != newKey && store["accounts"].HasKey(key)) - store["accounts"].Delete(key) - store["accounts"][newKey] := account - AccountMetadata_WriteStoreUnlocked(store) - AccountMetadata_ReleaseLock(hMutex) - return true + return AccountMetadata_SetDeviceFields(deviceAccount, {"language": language}, instance, fileName, "createdAt", "language") } AccountMetadata_SetLanguage(deviceAccount, language, instance := "", fileName := "") { - if (deviceAccount = "" || language = "") - return false - language := Trim(language) - if (language = "") - return false - - if (AccountMetadata_UseTempWrites()) { - account := AccountMetadata_NewAccount(instance, fileName) - account["deviceAccount"] := deviceAccount - if (instance != "") - account["instance"] := instance - if (fileName != "") - account["fileName"] := fileName - account["language"] := language - return AccountMetadata_SaveTempAccount(instance, fileName, account) - } - - hMutex := AccountMetadata_AcquireLock() - if (!hMutex) + if (deviceAccount = "" || language = "") return false - - store := AccountMetadata_ReadStoreUnlocked() - key := AccountMetadata_FindKey(store, instance, fileName, "", deviceAccount) - if (store["accounts"].HasKey(key)) { - account := store["accounts"][key] - } else { - account := AccountMetadata_NewAccount(instance, fileName) - account["deviceAccount"] := deviceAccount - } - - account["deviceAccount"] := deviceAccount - if (instance != "") - account["instance"] := instance - if (fileName != "") { - account["fileName"] := fileName - if (account["createdAt"] = "" || account["createdAt"] = "0") - account["createdAt"] := AccountMetadata_ExtractCreatedAt(fileName) - } - account["language"] := language - - newKey := AccountMetadata_DeviceKey(deviceAccount) - if (key != newKey && store["accounts"].HasKey(key)) - store["accounts"].Delete(key) - store["accounts"][newKey] := account - AccountMetadata_WriteStoreUnlocked(store) - AccountMetadata_ReleaseLock(hMutex) - return true + return AccountMetadata_SetDeviceFields(deviceAccount, {"language": language}, instance, fileName) } AccountMetadata_HasLanguage(deviceAccount, instance := "", fileName := "") { diff --git a/Scripts/Include/CardDetection.ahk b/Scripts/Include/CardDetection.ahk index 23dd7404..d7a05088 100644 --- a/Scripts/Include/CardDetection.ahk +++ b/Scripts/Include/CardDetection.ahk @@ -313,14 +313,7 @@ FoundStars(star, cards := "") { synthGPPath := "" highlightIds := isWishlist ? Wishlist_MatchIds(wishlistMatches) : "" if (GenerateSyntheticPackImage(cards, synthGPPath, highlightIds)) { - persistedGPPath := PersistSyntheticScreenshot(synthGPPath, star) - if (persistedGPPath != "") { - screenShot := persistedGPPath - if (FileExist(synthGPPath)) - FileDelete, %synthGPPath% - } else { - screenShot := synthGPPath - } + screenShot := CardDetection_PersistSynthetic(synthGPPath, star) isSyntheticGP := true } } @@ -409,14 +402,7 @@ GodPackFound(validity, cards := "", alreadyAtHome := false, preCapturedScreensho if (IsObject(cards) && cards.MaxIndex() > 0 && (botConfig.get("s4tUseSyntheticScreenshots") = "1" || cards.MaxIndex() > 6)) { synthGPPath := "" if (GenerateSyntheticPackImage(cards, synthGPPath)) { - persistedGPPath := PersistSyntheticScreenshot(synthGPPath, validity) - if (persistedGPPath != "") { - screenShot := persistedGPPath - if (FileExist(synthGPPath)) - FileDelete, %synthGPPath% - } else { - screenShot := synthGPPath - } + screenShot := CardDetection_PersistSynthetic(synthGPPath, validity) isSyntheticGP := true } } @@ -559,95 +545,30 @@ CardDetection_ResolveSavedXml(saveDir, ByRef accountFileName, expectedDeviceAcco ;------------------------------------------------------------------------------- ; FoundTradeable - Process found tradeable cards ;------------------------------------------------------------------------------- -FoundTradeable(found3Dmnd := 0, found4Dmnd := 0, found1Star := 0, foundGimmighoul := 0, foundCrown := 0, foundImmersive := 0, foundShiny1Star := 0, foundShiny2Star := 0, foundTrainer := 0, foundRainbow := 0, foundFullArt := 0) { - prof := Prof_Scope(A_ThisFunc) - global botConfig, session, dictionaryData - global screenShotFileName - - IniWrite, 0, % session.get("scriptIniFile"), UserSettings, DeadCheck - - session.set("keepAccount", true) - - foundTradeable := found3Dmnd + found4Dmnd + found1Star + foundGimmighoul + foundCrown + foundImmersive + foundShiny1Star + foundShiny2Star + foundTrainer + foundRainbow + foundFullArt - - if (botConfig.get("s4tWP") && botConfig.get("s4tWPMinCards") = 2 && foundTradeable < 2) { - CreateStatusMessage("s4t: insufficient cards (" . foundTradeable . "/2)",,,, false) - session.set("keepAccount", false) - return - } - session.set("s4tFoundTradeable", true) - - cardTypes := [] - cardCounts := [] - - if (found3Dmnd > 0) { - cardTypes.Push("3Diamond") - cardCounts.Push(found3Dmnd) - } - if (found4Dmnd > 0) { - cardTypes.Push("4Diamond") - cardCounts.Push(found4Dmnd) - } - if (found1Star > 0) { - cardTypes.Push("1Star") - cardCounts.Push(found1Star) - } - if (foundGimmighoul > 0) { - cardTypes.Push("Gimmighoul") - cardCounts.Push(foundGimmighoul) - } - if (foundCrown > 0) { - cardTypes.Push("Crown") - cardCounts.Push(foundCrown) - } - if (foundImmersive > 0) { - cardTypes.Push("Immersive") - cardCounts.Push(foundImmersive) - } - if (foundShiny1Star > 0) { - cardTypes.Push("Shiny1Star") - cardCounts.Push(foundShiny1Star) - } - if (foundShiny2Star > 0) { - cardTypes.Push("Shiny2Star") - cardCounts.Push(foundShiny2Star) - } - if (foundTrainer > 0) { - cardTypes.Push("Trainer") - cardCounts.Push(foundTrainer) - } - if (foundRainbow > 0) { - cardTypes.Push("Rainbow") - cardCounts.Push(foundRainbow) - } - if (foundFullArt > 0) { - cardTypes.Push("FullArt") - cardCounts.Push(foundFullArt) - } +; Saves the account XML for a tradeable find (or locates the injected one). Updates +; accountFileName and returns the XML path, or "" when there is no file to attach. +CardDetection_SaveTradeableAccount(deviceAccount, ByRef accountFileName) { + global session - deviceAccount := GetDeviceAccountFromXML() savedXmlPath := "" - if (!session.get("loadDir")) { - ; Create Bots mode: Check if XML already exists for this deviceAccount to prevent duplicates - if (session.get("deviceAccountXmlMap").HasKey(deviceAccount) && FileExist(session.get("deviceAccountXmlMap")[deviceAccount])) { - savedXmlPath := session.get("deviceAccountXmlMap")[deviceAccount] + ; Create Bots mode: reuse the XML of this device account if one exists to prevent duplicates + xmlMap := session.get("deviceAccountXmlMap") + if (xmlMap.HasKey(deviceAccount) && FileExist(xmlMap[deviceAccount])) { + savedXmlPath := xmlMap[deviceAccount] UpdateSavedXml(savedXmlPath) - ; Update accountFileName from saved path SplitPath, savedXmlPath, xmlFileName - session.set("accountFileName", xmlFileName) + accountFileName := xmlFileName + session.set("accountFileName", accountFileName) } else { - ; Create new XML only if one doesn't exist saveAccount("All", savedXmlPath) - ; Extract filename and update accountFileName if (savedXmlPath) { SplitPath, savedXmlPath, xmlFileName - session.set("accountFileName", xmlFileName) - - ; Store mapping for future reference - session.get("deviceAccountXmlMap")[deviceAccount] := savedXmlPath + accountFileName := xmlFileName + session.set("accountFileName", accountFileName) + xmlMap[deviceAccount] := savedXmlPath } } @@ -656,21 +577,116 @@ FoundTradeable(found3Dmnd := 0, found4Dmnd := 0, found1Star := 0, foundGimmighou tradeableData.deviceAccount := deviceAccount session.get("s4tPendingTradeables").Push(tradeableData) } else { - ; Inject mode: Use the current accountFileName (which may have new name due to pack count) - ; and construct the full path from it + ; Inject mode: use the current accountFileName (it may have a new name due to the pack count) saveDir := A_ScriptDir "\..\Accounts\Saved\" . session.get("scriptName") - accountFileName := session.get("accountFileName") savedXmlPath := CardDetection_ResolveSavedXml(saveDir, accountFileName, deviceAccount) if (savedXmlPath != "") session.set("accountFileName", accountFileName) - ; verification if (!FileExist(savedXmlPath)) { CreateStatusMessage("Warning: Could not find account XML file for attachment", "", 0, 0, false) - LogWarn("FoundTradeable: Could not find XML file. accountFileName=" . session.get("accountFileName") . ", savedXmlPath=" . savedXmlPath, "S4T.txt") + LogWarn("FoundTradeable: Could not find XML file. accountFileName=" . accountFileName . ", savedXmlPath=" . savedXmlPath, "S4T.txt") savedXmlPath := "" ; Clear it so we don't try to attach a non-existent file } } + return savedXmlPath +} + +; Fills cardTypes/cardCounts from the found card counts and returns the total. +CardDetection_BuildTypeLists(foundCards, ByRef cardTypes, ByRef cardCounts) { + cardTypes := [] + cardCounts := [] + total := 0 + for _, type in ["1Diamond", "2Diamond", "3Diamond", "4Diamond", "1Star", "FullArt", "Rainbow", "Trainer", "Immersive", "Crown", "Shiny1Star", "Shiny2Star", "Wishlist"] { + count := foundCards.HasKey(type) ? foundCards[type] : 0 + total += count + + if (count > 0) { + cardTypes.Push(type) + cardCounts.Push(count) + } + } + return total +} + +; Moves a generated synthetic image into the screenshots folder. Returns the new path, +; or the original temp path if it could not be moved. +CardDetection_PersistSynthetic(synthPath, fileType, subDir := "") { + persistedPath := PersistSyntheticScreenshot(synthPath, fileType, subDir) + if (persistedPath = "") + return synthPath + if (FileExist(synthPath)) + FileDelete, %synthPath% + return persistedPath +} + +; Generates a synthetic pack image for tradeable cards, honoring the keep setting. +; Returns the image path, or "" if no image could be generated. +CardDetection_CreateSyntheticImage(cards, highlightIds, maxCols) { + global botConfig + + synthPath := "" + if (!GenerateSyntheticPackImage(cards, synthPath, highlightIds, maxCols)) + return "" + if (botConfig.get("s4tKeepSyntheticScreenshots")) + synthPath := CardDetection_PersistSynthetic(synthPath, "Tradeable", "Trades") + return synthPath +} + +; Posts a pack result to the S4T webhook unless silent mode is on or no webhook is set. +; wishlistMatches may be "" when wishlist matches are not part of the message. +CardDetection_SendS4TDiscord(title, emoji, packName, packDetailsMessage, wishlistMatches, accountFileName, screenShot, savedXmlPath, footer := "") { + global botConfig, session + + if (botConfig.get("s4tSilent") || !botConfig.get("s4tDiscordWebhookURL")) + return + + discordMessage := S4T_BuildDiscordHeader(title, session.get("scriptName"), session.get("packsInPool"), packName, emoji) . "\n" + if (packDetailsMessage != "") + discordMessage .= "\n" . packDetailsMessage . "\n\n" + if (botConfig.get("s4tWishlist") && IsObject(wishlistMatches) && wishlistMatches.MaxIndex() > 0) { + sparkle := Chr(0x2728) + discordMessage .= sparkle . " WISHLIST: " . Wishlist_FormatNames(wishlistMatches) . " " . sparkle . "\n" + } + discordMessage .= "File name: " . accountFileName . footer + + xmlFileToSend := "" + if (botConfig.get("s4tSendAccountXml") && savedXmlPath && FileExist(savedXmlPath)) + xmlFileToSend := savedXmlPath + + LogToDiscord(discordMessage, screenShot, true, xmlFileToSend,, botConfig.get("s4tDiscordWebhookURL"), botConfig.get("s4tDiscordUserId")) +} + +FoundTradeable(found3Dmnd := 0, found4Dmnd := 0, found1Star := 0, foundGimmighoul := 0, foundCrown := 0, foundImmersive := 0, foundShiny1Star := 0, foundShiny2Star := 0, foundTrainer := 0, foundRainbow := 0, foundFullArt := 0) { + prof := Prof_Scope(A_ThisFunc) + global botConfig, session, dictionaryData + global screenShotFileName + + IniWrite, 0, % session.get("scriptIniFile"), UserSettings, DeadCheck + + session.set("keepAccount", true) + + foundTradeable := found3Dmnd + found4Dmnd + found1Star + foundGimmighoul + foundCrown + foundImmersive + foundShiny1Star + foundShiny2Star + foundTrainer + foundRainbow + foundFullArt + + if (botConfig.get("s4tWP") && botConfig.get("s4tWPMinCards") = 2 && foundTradeable < 2) { + CreateStatusMessage("s4t: insufficient cards (" . foundTradeable . "/2)",,,, false) + session.set("keepAccount", false) + return + } + session.set("s4tFoundTradeable", true) + + cardTypes := [] + cardCounts := [] + for _, entry in [["3Diamond", found3Dmnd], ["4Diamond", found4Dmnd], ["1Star", found1Star], ["Gimmighoul", foundGimmighoul], ["Crown", foundCrown], ["Immersive", foundImmersive], ["Shiny1Star", foundShiny1Star], ["Shiny2Star", foundShiny2Star], ["Trainer", foundTrainer], ["Rainbow", foundRainbow], ["FullArt", foundFullArt]] { + if (entry[2] > 0) { + cardTypes.Push(entry[1]) + cardCounts.Push(entry[2]) + } + } + + deviceAccount := GetDeviceAccountFromXML() + accountFileName := session.get("accountFileName") + savedXmlPath := CardDetection_SaveTradeableAccount(deviceAccount, accountFileName) screenShot := Screenshot("Tradeable", "Trades", screenShotFileName) @@ -683,30 +699,15 @@ FoundTradeable(found3Dmnd := 0, found4Dmnd := 0, found1Star := 0, foundGimmighou logMessage := statusMessage . " in instance: " . session.get("scriptName") . " (" . session.get("packsInPool") . " packs, " . dictionaryData[botConfig.get("defaultBotLanguage")][session.get("openPack")] . ") Screenshot file: " . screenShotFileName LogInfo(logMessage, "S4T.txt") - if (!botConfig.get("s4tSilent") && botConfig.get("s4tDiscordWebhookURL")) { - ; Legacy path has only count vars (no card IDs/rarity array). Reuse the - ; shared builder via count-only fallback: Discord format stays consistent - ; with the modern paths, just without per-card names. - foundCards := { "3Diamond": found3Dmnd, "4Diamond": found4Dmnd, "1Star": found1Star - , "Crown": foundCrown, "Immersive": foundImmersive - , "Shiny1Star": foundShiny1Star, "Shiny2Star": foundShiny2Star - , "Trainer": foundTrainer, "Rainbow": foundRainbow, "FullArt": foundFullArt } - packDetailsMessage := CardName_BuildFoundBlock("", "", foundCards) - - discordMessage := S4T_BuildDiscordHeader("Pack Opening results", session.get("scriptName"), session.get("packsInPool"), session.get("openPack"), Chr(0x1F4E6)) . "\n" - if (packDetailsMessage != "") - discordMessage .= "\n" . packDetailsMessage . "\n\n" - discordMessage .= "File name: " . session.get("accountFileName") - - ; Prepare XML file path for attachment - xmlFileToSend := "" - ; NOW savedXmlPath will have the correct path with the updated filename! - if (botConfig.get("s4tSendAccountXml") && savedXmlPath && FileExist(savedXmlPath)) { - xmlFileToSend := savedXmlPath - } - - LogToDiscord(discordMessage, screenShot, true, xmlFileToSend,, botConfig.get("s4tDiscordWebhookURL"), botConfig.get("s4tDiscordUserId")) - } + ; Legacy path has only count vars (no card IDs/rarity array). Reuse the shared + ; builder via count-only fallback: Discord format stays consistent with the + ; modern paths, just without per-card names. + foundCards := { "3Diamond": found3Dmnd, "4Diamond": found4Dmnd, "1Star": found1Star + , "Crown": foundCrown, "Immersive": foundImmersive + , "Shiny1Star": foundShiny1Star, "Shiny2Star": foundShiny2Star + , "Trainer": foundTrainer, "Rainbow": foundRainbow, "FullArt": foundFullArt } + packDetailsMessage := CardName_BuildFoundBlock("", "", foundCards) + CardDetection_SendS4TDiscord("Pack Opening results", Chr(0x1F4E6), session.get("openPack"), packDetailsMessage, "", accountFileName, screenShot, savedXmlPath) return } @@ -763,90 +764,13 @@ CheckCardsSimple(result) { session.set("keepAccount", true) session.set("s4tFoundTradeable", true) - scriptName := session.get("scriptName") - winTitle := session.get("winTitle") - packsInPool := session.get("packsInPool") - openPack := session.get("openPack") - loadDir := session.get("loadDir") - accountFileName := session.get("accountFileName") - - order := ["1Diamond", "2Diamond", "3Diamond", "4Diamond", "1Star", "FullArt", "Rainbow", "Trainer", "Immersive", "Crown", "Shiny1Star", "Shiny2Star", "Wishlist"] - out := "" - for key, value in foundCards - out .= key ": " value "`n" - - displayNames := {} - displayNames["1Diamond"] := "One Diamond" - displayNames["2Diamond"] := "Two Diamond" - displayNames["3Diamond"] := "Three Diamond" - displayNames["4Diamond"] := "Four Diamond EX" - displayNames["1Star"] := "One Star" - displayNames["Crown"] := "Crown" - displayNames["Immersive"] := "Immersive" - displayNames["Shiny1Star"] := "Shiny 1-Star" - displayNames["Shiny2Star"] := "Shiny 2-Star" - displayNames["Trainer"] := "Trainer" - displayNames["Rainbow"] := "Rainbow" - displayNames["FullArt"] := "Full Art" - displayNames["Wishlist"] := "Wishlist" - - foundTradeable := 0 - cardTypes := [] - cardCounts := [] - - for _, type in order { - count := foundCards.HasKey(type) ? foundCards[type] : 0 - foundTradeable += count - - if (count > 0) { - cardTypes.Push(type) - cardCounts.Push(count) - } - } - ; Build the Discord rarity summary with per-rarity emoji + card names. ; CardName_BuildFoundBlock handles the cards/rarity-missing case gracefully. packDetailsMessage := CardName_BuildFoundBlock(cards, rarity, foundCards) deviceAccount := GetDeviceAccountFromXML() - savedXmlPath := "" - - if (!loadDir) { - if (session.get("deviceAccountXmlMap").HasKey(deviceAccount) && FileExist(session.get("deviceAccountXmlMap")[deviceAccount])) { - savedXmlPath := session.get("deviceAccountXmlMap")[deviceAccount] - UpdateSavedXml(savedXmlPath) - - SplitPath, savedXmlPath, xmlFileName - accountFileName := xmlFileName - session.set("accountFileName", accountFileName) - } else { - saveAccount("All", savedXmlPath) - - if (savedXmlPath) { - SplitPath, savedXmlPath, xmlFileName - accountFileName := xmlFileName - session.set("accountFileName", accountFileName) - session.get("deviceAccountXmlMap")[deviceAccount] := savedXmlPath - } - } - - tradeableData := {} - tradeableData.xmlPath := savedXmlPath - tradeableData.deviceAccount := deviceAccount - session.get("s4tPendingTradeables").Push(tradeableData) - } else { - saveDir := A_ScriptDir "\..\Accounts\Saved\" . scriptName - savedXmlPath := CardDetection_ResolveSavedXml(saveDir, accountFileName, deviceAccount) - if (savedXmlPath != "") - session.set("accountFileName", accountFileName) - - if (!FileExist(savedXmlPath)) { - CreateStatusMessage("Warning: Could not find account XML file for attachment", "", 0, 0, false) - LogWarn("FoundTradeable: Could not find XML file. accountFileName=" . accountFileName . ", savedXmlPath=" . savedXmlPath, "S4T.txt") - savedXmlPath := "" - } - } + accountFileName := session.get("accountFileName") + savedXmlPath := CardDetection_SaveTradeableAccount(deviceAccount, accountFileName) - statusMessage := "Giftpack cards found" wishlistMatches := session.get("wishlistMatches") if (!IsObject(wishlistMatches)) wishlistMatches := [] @@ -855,35 +779,13 @@ CheckCardsSimple(result) { filteredCards := FilterCardsByS4T(cards, rarity) ; Try to generate a synthetic image from card IDs synthScreenShot := "" - if (filteredCards.MaxIndex() > 0) { - highlightIds := hasWishlist ? Wishlist_MatchIds(wishlistMatches) : "" - if (GenerateSyntheticPackImage(filteredCards, synthScreenShot, highlightIds, 6) && botConfig.get("s4tKeepSyntheticScreenshots")) { - persistedGiftPath := PersistSyntheticScreenshot(synthScreenShot, "Tradeable", "Trades") - if (persistedGiftPath != "") { - if (FileExist(synthScreenShot)) - FileDelete, %synthScreenShot% - synthScreenShot := persistedGiftPath - } - } - } + if (filteredCards.MaxIndex() > 0) + synthScreenShot := CardDetection_CreateSyntheticImage(filteredCards, hasWishlist ? Wishlist_MatchIds(wishlistMatches) : "", 6) - if (!botConfig.get("s4tSilent") && botConfig.get("s4tDiscordWebhookURL")) { - packName := (openPack != "") ? openPack : pack - discordMessage := S4T_BuildDiscordHeader("Gift Pack Opening results", scriptName, packsInPool, packName, Chr(0x1F381)) . "\n" - if (packDetailsMessage != "") - discordMessage .= "\n" . packDetailsMessage . "\n\n" - if (hasWishlist) { - sparkle := Chr(0x2728) - discordMessage .= sparkle . " WISHLIST: " . Wishlist_FormatNames(wishlistMatches) . " " . sparkle . "\n" - } - discordMessage .= "File name: " . accountFileName . "\n" - - xmlFileToSend := "" - if (botConfig.get("s4tSendAccountXml") && savedXmlPath && FileExist(savedXmlPath)) - xmlFileToSend := savedXmlPath + openPack := session.get("openPack") + packName := (openPack != "") ? openPack : pack + CardDetection_SendS4TDiscord("Gift Pack Opening results", Chr(0x1F381), packName, packDetailsMessage, wishlistMatches, accountFileName, synthScreenShot, savedXmlPath, "\n") - LogToDiscord(discordMessage, synthScreenShot, true, xmlFileToSend,, botConfig.get("s4tDiscordWebhookURL"), botConfig.get("s4tDiscordUserId")) - } ; Delete synthetic image after sending if (synthScreenShot && !botConfig.get("s4tKeepSyntheticScreenshots") && FileExist(synthScreenShot)) FileDelete, %synthScreenShot% @@ -899,45 +801,13 @@ FoundTradeableNew(foundCards, pack := "", cards := "", rarity := "", isTenPackOp session.set("keepAccount", true) scriptName := session.get("scriptName") - winTitle := session.get("winTitle") packsInPool := session.get("packsInPool") openPack := session.get("openPack") - loadDir := session.get("loadDir") accountFileName := session.get("accountFileName") - order := ["1Diamond", "2Diamond", "3Diamond", "4Diamond", "1Star", "FullArt", "Rainbow", "Trainer", "Immersive", "Crown", "Shiny1Star", "Shiny2Star", "Wishlist"] - out := "" - for key, value in foundCards - out .= key ": " value "`n" - - displayNames := {} - displayNames["1Diamond"] := "One Diamond" - displayNames["2Diamond"] := "Two Diamond" - displayNames["3Diamond"] := "Three Diamond" - displayNames["4Diamond"] := "Four Diamond EX" - displayNames["1Star"] := "One Star" - displayNames["Crown"] := "Crown" - displayNames["Immersive"] := "Immersive" - displayNames["Shiny1Star"] := "Shiny 1-Star" - displayNames["Shiny2Star"] := "Shiny 2-Star" - displayNames["Trainer"] := "Trainer" - displayNames["Rainbow"] := "Rainbow" - displayNames["FullArt"] := "Full Art" - displayNames["Wishlist"] := "Wishlist" - - foundTradeable := 0 cardTypes := [] cardCounts := [] - - for _, type in order { - count := foundCards.HasKey(type) ? foundCards[type] : 0 - foundTradeable += count - - if (count > 0) { - cardTypes.Push(type) - cardCounts.Push(count) - } - } + foundTradeable := CardDetection_BuildTypeLists(foundCards, cardTypes, cardCounts) ; Build the Discord rarity summary with per-rarity emoji + card names. ; CardName_BuildFoundBlock handles the cards/rarity-missing case gracefully. @@ -951,43 +821,7 @@ FoundTradeableNew(foundCards, pack := "", cards := "", rarity := "", isTenPackOp session.set("s4tFoundTradeable", true) deviceAccount := GetDeviceAccountFromXML() - savedXmlPath := "" - - if (!loadDir) { - if (session.get("deviceAccountXmlMap").HasKey(deviceAccount) && FileExist(session.get("deviceAccountXmlMap")[deviceAccount])) { - savedXmlPath := session.get("deviceAccountXmlMap")[deviceAccount] - UpdateSavedXml(savedXmlPath) - - SplitPath, savedXmlPath, xmlFileName - accountFileName := xmlFileName - session.set("accountFileName", accountFileName) - } else { - saveAccount("All", savedXmlPath) - - if (savedXmlPath) { - SplitPath, savedXmlPath, xmlFileName - accountFileName := xmlFileName - session.set("accountFileName", accountFileName) - session.get("deviceAccountXmlMap")[deviceAccount] := savedXmlPath - } - } - - tradeableData := {} - tradeableData.xmlPath := savedXmlPath - tradeableData.deviceAccount := deviceAccount - session.get("s4tPendingTradeables").Push(tradeableData) - } else { - saveDir := A_ScriptDir "\..\Accounts\Saved\" . scriptName - savedXmlPath := CardDetection_ResolveSavedXml(saveDir, accountFileName, deviceAccount) - if (savedXmlPath != "") - session.set("accountFileName", accountFileName) - - if (!FileExist(savedXmlPath)) { - CreateStatusMessage("Warning: Could not find account XML file for attachment", "", 0, 0, false) - LogWarn("FoundTradeable: Could not find XML file. accountFileName=" . accountFileName . ", savedXmlPath=" . savedXmlPath, "S4T.txt") - savedXmlPath := "" - } - } + savedXmlPath := CardDetection_SaveTradeableAccount(deviceAccount, accountFileName) wishlistMatches := session.get("wishlistMatches") if (!IsObject(wishlistMatches)) @@ -997,26 +831,15 @@ FoundTradeableNew(foundCards, pack := "", cards := "", rarity := "", isTenPackOp screenShotFileName := "" isSyntheticImage := false ; Try to generate a synthetic image from card IDs (avoids storing real screenshots on disk). - ; Only multi-pull (10-pack) needs s4t filtering on the synth image — single-pull packs + ; Only multi-pull (10-pack) needs s4t filtering on the synth image - single-pull packs ; show all 5 cards regardless of rarity. The rarity array itself is always honored for ; the webhook name lookup (CardName_BuildFoundBlock). syntheticCards := (isTenPackOpening && IsObject(rarity)) ? FilterCardsByS4T(cards, rarity) : cards if (isTenPackOpening && IsObject(rarity) && (!IsObject(syntheticCards) || syntheticCards.MaxIndex() = "") && hasWishlist) syntheticCards := FilterCardsByWishlistMatches(cards, wishlistMatches) if (IsObject(syntheticCards) && syntheticCards.MaxIndex() > 0 && (botConfig.get("s4tUseSyntheticScreenshots") = "1" || cards.MaxIndex() > 6)) { - synthPath := "" - highlightIds := hasWishlist ? Wishlist_MatchIds(wishlistMatches) : "" - synthCols := isTenPackOpening ? 6 : 3 - if (GenerateSyntheticPackImage(syntheticCards, synthPath, highlightIds, synthCols)) { - if (botConfig.get("s4tKeepSyntheticScreenshots")) { - persistedTradePath := PersistSyntheticScreenshot(synthPath, "Tradeable", "Trades") - if (persistedTradePath != "") { - if (FileExist(synthPath)) - FileDelete, %synthPath% - synthPath := persistedTradePath - } - } - + synthPath := CardDetection_CreateSyntheticImage(syntheticCards, hasWishlist ? Wishlist_MatchIds(wishlistMatches) : "", isTenPackOpening ? 6 : 3) + if (synthPath != "") { screenShot := synthPath SplitPath, synthPath, screenShotFileName isSyntheticImage := true @@ -1036,24 +859,8 @@ FoundTradeableNew(foundCards, pack := "", cards := "", rarity := "", isTenPackOp logMessage := statusMessage . " in instance: " . scriptName . " (" . packsInPool . " packs, " . packName . ") Screenshot file: " . screenShotFileName LogInfo(logMessage, "S4T.txt") - if (!botConfig.get("s4tSilent") && botConfig.get("s4tDiscordWebhookURL")) { - headerTitle := isTenPackOpening ? "10-Packs Opening results" : "Pack Opening results" - headerEmoji := Chr(0x1F4E6) - discordMessage := S4T_BuildDiscordHeader(headerTitle, scriptName, packsInPool, packName, headerEmoji) . "\n" - if (packDetailsMessage != "") - discordMessage .= "\n" . packDetailsMessage . "\n\n" - if (hasWishlist) { - sparkle := Chr(0x2728) - discordMessage .= sparkle . " WISHLIST: " . Wishlist_FormatNames(wishlistMatches) . " " . sparkle . "\n" - } - discordMessage .= "File name: " . accountFileName - - xmlFileToSend := "" - if (botConfig.get("s4tSendAccountXml") && savedXmlPath && FileExist(savedXmlPath)) - xmlFileToSend := savedXmlPath - - LogToDiscord(discordMessage, screenShot, true, xmlFileToSend,, botConfig.get("s4tDiscordWebhookURL"), botConfig.get("s4tDiscordUserId")) - } + headerTitle := isTenPackOpening ? "10-Packs Opening results" : "Pack Opening results" + CardDetection_SendS4TDiscord(headerTitle, Chr(0x1F4E6), packName, packDetailsMessage, wishlistMatches, accountFileName, screenShot, savedXmlPath) ; Delete synthetic image after sending (avoids accumulating screenshots on disk) if (isSyntheticImage && !botConfig.get("s4tKeepSyntheticScreenshots") && screenShot && FileExist(screenShot)) From 696de490ffbd80626d3d685e675788de4ee58cce Mon Sep 17 00:00:00 2001 From: Lean Date: Mon, 12 Oct 2026 21:12:04 +0200 Subject: [PATCH 09/10] Fix packdata --- Data/packdata.dat | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Data/packdata.dat b/Data/packdata.dat index ac621e4c..39e2a8ff 100644 --- a/Data/packdata.dat +++ b/Data/packdata.dat @@ -1,5 +1,5 @@ # Pack list in Home screen -Home:Left|RulerOfTheSkies,Middle|DeluxeB,Right|TeamRocketsAmbition +Home:Left|RulerOfTheSkies,Middle|DeluxePackMega,Right|TeamRocketsAmbition # Pack data(Structure) # PackID, Series Type, Number of packs in the set, Location of extension window, y Pos, Scroll Type, Expansion ID @@ -22,15 +22,15 @@ Pack:Springs|A|1|Col-Second|298|0|A4a Pack:Deluxe|A|1|Col-First|298|0|A4b # B Series -Pack:MegaGyarados|B|3|Col-First-Left|405|1|B1 Pack:MegaBlaziken|B|3|Col-First-Middle|405|1|B1 +Pack:MegaGyarados|B|3|Col-First-Left|405|1|B1 Pack:MegaAltaria|B|3|Col-First-Right|405|1|B1 Pack:CrimsonBlaze|B|1|Col-Second|238|1|B1a Pack:Parade|B|1|Col-First|238|1|B2 Pack:PaldeanWonders|B|1|Col-Second|107|1|B2a -Pack:MegaShine|B|1|Col-First|107|2|B2b +Pack:MegaShine|B|1|Col-First|107|1|B2b Pack:PulsingAura|B|1|Col-Second|440|2|B3 -Pack:ParadoxDrive|B|1|Col-First|440|0|B3a +Pack:ParadoxDrive|B|1|Col-First|440|2|B3a Pack:EverydayWonders|B|1|Col-Second|434|0|B3b Pack:RulerOfTheSkies|B|1|Col-First|434|0|B4 Pack:TeamRocketsAmbition|B|1|Col-Second|298|0|B4a From 455e82c01ee57dbec84e08907daf79fa0a27eb5e Mon Sep 17 00:00:00 2001 From: Lean Date: Thu, 15 Oct 2026 14:12:08 +0200 Subject: [PATCH 10/10] Fix packdata --- Data/packdata.dat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Data/packdata.dat b/Data/packdata.dat index 39e2a8ff..287c4577 100644 --- a/Data/packdata.dat +++ b/Data/packdata.dat @@ -1,5 +1,5 @@ # Pack list in Home screen -Home:Left|RulerOfTheSkies,Middle|DeluxePackMega,Right|TeamRocketsAmbition +Home:Left|RulerOfTheSkies,Middle|DeluxeB,Right|TeamRocketsAmbition # Pack data(Structure) # PackID, Series Type, Number of packs in the set, Location of extension window, y Pos, Scroll Type, Expansion ID