Created
November 25, 2023 22:17
-
-
Save josepdecid/0cb8483552fabd93cff1e6eb57840801 to your computer and use it in GitHub Desktop.
Small script that auto-plans Dragon's Lair using the minimum number of dependencies possible, Win32 and Numpy. I've only been able to test it with the Steam version and it works playing with the "Arcade Cabinet" and "Move Guide" options set to ON. Some of the stuff is slightly hardcoded and values are extracted from a standard 1920x1080 screen.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import time | |
import numpy as np | |
import win32api | |
import win32gui | |
import win32ui | |
import win32con | |
from PIL import Image | |
def get_window_screenshot(): | |
# Get the window handle | |
hwnd = win32gui.FindWindow(None, "Dragon's Lair") | |
if hwnd == 0: | |
raise Exception(f"Window not found") | |
# Bring the window to the foreground | |
win32gui.SetForegroundWindow(hwnd) | |
win32gui.ShowWindow(hwnd, win32con.SW_RESTORE) | |
# Get the window's client area | |
left, top, right, bot = win32gui.GetClientRect(hwnd) | |
w = right - left | |
h = bot - top | |
# Get the device context | |
hwndDC = win32gui.GetWindowDC(hwnd) | |
mfcDC = win32ui.CreateDCFromHandle(hwndDC) | |
saveDC = mfcDC.CreateCompatibleDC() | |
# Create a bitmap object | |
saveBitMap = win32ui.CreateBitmap() | |
saveBitMap.CreateCompatibleBitmap(mfcDC, w, h) | |
saveDC.SelectObject(saveBitMap) | |
# Copy the screen into our memory device context | |
result = saveDC.BitBlt((0, 0), (w, h), mfcDC, (left, top), win32con.SRCCOPY) | |
# Convert the bitmap to a PIL image | |
bmpinfo = saveBitMap.GetInfo() | |
bmpstr = saveBitMap.GetBitmapBits(True) | |
im = Image.frombuffer( | |
"RGB", | |
(bmpinfo["bmWidth"], bmpinfo["bmHeight"]), | |
bmpstr, "raw", "BGRX", 0, 1) | |
win32gui.DeleteObject(saveBitMap.GetHandle()) | |
saveDC.DeleteDC() | |
mfcDC.DeleteDC() | |
win32gui.ReleaseDC(hwnd, hwndDC) | |
return im | |
def process_image(raw_image) -> str: | |
np_image = np.array(raw_image) | |
gray_image = np.dot(np_image[...,:3], [0.2989, 0.5870, 0.1140]) | |
bw_image = np.where(gray_image >= 128, True, False) | |
left_action = bw_image[625, 100] | |
right_action = bw_image[625, 290] | |
up_action = bw_image[525, 195] | |
down_action = bw_image[725, 195] | |
if sum([left_action, right_action, up_action, down_action]) > 1: | |
return None | |
if left_action: | |
return win32con.VK_LEFT | |
if right_action: | |
return win32con.VK_RIGHT | |
if up_action: | |
return win32con.VK_UP | |
if down_action: | |
return win32con.VK_DOWN | |
attack_region = bw_image[820:860, 940:980] | |
white_pixels_ratio = np.sum(attack_region) \ | |
/ (attack_region.shape[0] * attack_region.shape[1]) | |
if white_pixels_ratio > 0.75 and white_pixels_ratio < 0.95: | |
return win32con.VK_SPACE | |
return None | |
def main(): | |
while True: | |
screenshot = get_window_screenshot() | |
action = process_image(screenshot) | |
if action is not None: | |
win32api.keybd_event(action, 0, 0, 0) | |
time.sleep(0.025) | |
win32api.keybd_event(action, 0, win32con.KEYEVENTF_KEYUP, 0) | |
time.sleep(0.1) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment