# Drive the game for automated tests: start it, click, press keys, take # screenshots. Dot-source it: . .\tools\game-input.ps1 # # Start-Sacred # launcher, returns the sacred.exe process # Click-At 1720 779 # screen pixels (left click) # Press-Key 0x1B # virtual key (ESC) # Save-Screen screenshots\x.png # primary monitor area of the game # Stop-Sacred $game # only the given process # # Input goes to the foreground window: don't touch mouse/keyboard meanwhile. Add-Type -AssemblyName System.Drawing Add-Type -Namespace SacredRe -Name Input -MemberDefinition @' [DllImport("user32.dll")] public static extern bool SetCursorPos(int x, int y); [DllImport("user32.dll")] public static extern void mouse_event(uint flags, uint dx, uint dy, uint data, System.UIntPtr extra); [DllImport("user32.dll")] public static extern void keybd_event(byte vk, byte scan, uint flags, System.UIntPtr extra); [DllImport("user32.dll")] public static extern bool SetForegroundWindow(System.IntPtr window); '@ $script:Root = Split-Path $PSScriptRoot # The game pauses its start-up until its window is active, and Windows does # not hand the foreground to a window started from a background process; a # synthetic Alt press lifts that restriction for SetForegroundWindow. function Focus-Sacred($Game) { $Game.Refresh() if ($Game.MainWindowHandle -eq [IntPtr]::Zero) { return $false } [SacredRe.Input]::keybd_event(0x12, 0, 0, [UIntPtr]::Zero) [SacredRe.Input]::keybd_event(0x12, 0, 2, [UIntPtr]::Zero) [SacredRe.Input]::SetForegroundWindow($Game.MainWindowHandle) } function Start-Sacred([string]$Arguments = "log", [int]$WaitSeconds = 18) { if (Get-Process sacred -ErrorAction SilentlyContinue) { throw "sacred.exe is already running" } $null = Start-Process (Join-Path $script:Root "build\x86-debug\sacred_re.exe") -ArgumentList $Arguments $game = $null for ($i = 0; $i -lt $WaitSeconds * 4; $i++) { Start-Sleep -Milliseconds 250 if (-not $game) { $game = Get-Process sacred -ErrorAction SilentlyContinue | Select-Object -First 1 } if ($game) { Focus-Sacred $game | Out-Null } } if (-not $game -or $game.HasExited) { throw "sacred.exe did not start (or crashed)" } $game } function Stop-Sacred($Game) { if ($Game -and -not $Game.HasExited) { Stop-Process -Id $Game.Id -Force $Game.WaitForExit(10000) | Out-Null } } function Click-At([int]$X, [int]$Y, [int]$AfterMs = 800) { [SacredRe.Input]::SetCursorPos($X, $Y) | Out-Null Start-Sleep -Milliseconds 150 [SacredRe.Input]::mouse_event(0x0002, 0, 0, 0, [UIntPtr]::Zero) # left down Start-Sleep -Milliseconds 80 [SacredRe.Input]::mouse_event(0x0004, 0, 0, 0, [UIntPtr]::Zero) # left up Start-Sleep -Milliseconds $AfterMs } function Press-Key([byte]$VirtualKey, [int]$AfterMs = 800) { [SacredRe.Input]::keybd_event($VirtualKey, 0, 0, [UIntPtr]::Zero) Start-Sleep -Milliseconds 80 [SacredRe.Input]::keybd_event($VirtualKey, 0, 2, [UIntPtr]::Zero) # key up Start-Sleep -Milliseconds $AfterMs } function Save-Screen([string]$Path, [int]$Width = 3440, [int]$Height = 1440) { $bitmap = New-Object System.Drawing.Bitmap $Width, $Height $graphics = [System.Drawing.Graphics]::FromImage($bitmap) $graphics.CopyFromScreen(0, 0, 0, 0, $bitmap.Size) $full = if ([IO.Path]::IsPathRooted($Path)) { $Path } else { Join-Path $script:Root $Path } $bitmap.Save($full) $graphics.Dispose(); $bitmap.Dispose() $full }