Skip to content

Instantly share code, notes, and snippets.

@josepdecid
Created November 25, 2023 22:17
Show Gist options
  • Save josepdecid/0cb8483552fabd93cff1e6eb57840801 to your computer and use it in GitHub Desktop.
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.
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