Skip to content

Commit

Permalink
v1.4.2
Browse files Browse the repository at this point in the history
  • Loading branch information
Psycho-Marcus committed Sep 12, 2024
1 parent 8f3079d commit e213f26
Show file tree
Hide file tree
Showing 17 changed files with 1,233 additions and 464 deletions.
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,26 @@ The data format is specifically designed for [WuWa Tracker](https://wuwatracker.
- English

- **Features:**
- Scan Characters
- Scan Weapons
- Scan Echoes
- Scan Development Items
- Scan Resources
- Scan Achievements
- Edit/View inventory data

## To-Do List
- [ ] Character Scanner
- [ ] Weapons Scanner
- [ ] Echoes Scanner
- [ ] Achievements Scanner
- [x] Character Scanner (no echo)
- [x] Weapons Scanner
- [x] Echoes Scanner (really slow at the moment)
- [x] Achievements Scanner
- [ ] Auto Updater
- [ ] Support for additional in-game languages
- [ ] Support for more software languages
- [ ] Improve text recognition accuracy
- [ ] Improve logs
- [ ] Optimize releases size
- [ ] Rewrite the code (after all tasks are complete; this is urgently needed)

## Tutorial

Expand Down
26 changes: 10 additions & 16 deletions game/menu.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import numpy as np
import mss
import time
import logging
import cv2
import pytesseract

import properties.config
from game.screenSize import WindowManager
from game.foreground import WindowFocusManager
from scraping.utils import pressEscape
from scraping.utils import (
scaleWidth, scaleHeight, screenshot
)

logger = logging.getLogger('MainMenuController')

Expand All @@ -22,19 +21,15 @@ def isMenu(self) -> bool:
Returns:
bool: True if the main menu is detected, False otherwise.
"""
screenshot_region = {
'left': int(140 / 1920 * WindowManager.getWidth()),
'top': int(45 / 1080 * WindowManager.getHeight()),
'width': int(140 / 1920 * WindowManager.getWidth()),
'height': int(30 / 1080 * WindowManager.getHeight())
}

try:
with mss.mss() as sct:
screenshot = np.array(sct.grab(screenshot_region), dtype=np.uint8)
screenshot = cv2.cvtColor(screenshot, cv2.COLOR_BGR2RGB)
image = screenshot(
scaleWidth(140, WindowManager.getWidth()),
scaleHeight(45, WindowManager.getHeight()),
scaleWidth(140, WindowManager.getWidth()),
scaleHeight(30, WindowManager.getHeight())
)

result = pytesseract.image_to_string(screenshot).strip().lower()
result = pytesseract.image_to_string(image).strip().lower()
logger.debug(f"Detected text from screenshot: '{result}'")

return result in ['terminal', 'terminat']
Expand Down Expand Up @@ -64,7 +59,6 @@ def isInMainMenu(self):
if not self.isMenu():
return 'error', 'Error', 'Not in the main menu. Press ESC in-game and rerun the scanner.'

pressEscape()
return '', '', ''

except Exception as e:
Expand Down
2 changes: 1 addition & 1 deletion main.spec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# -*- mode: python ; coding: utf-8 -*-
from PyInstaller.utils.win32 import versioninfo as vi

version = (1, 1, 0, 0)
version = (1, 4, 2, 0)

version_file = vi.VSVersionInfo(
ffi=vi.FixedFileInfo(
Expand Down
9 changes: 4 additions & 5 deletions properties/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,9 @@
# Default values
PROCESS_NAME = 'Client-Win64-Shipping.exe'
WINDOW_NAME = 'Wuthering Waves'
START_DATE = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
DPI_SCALING = user32.GetDpiForWindow(user32.GetForegroundWindow()) / 96.0
INVENTORY = dict()
INVENTORY = {'date': str(), 'items': dict()}
FAILED: list[dict] = list()
CHARACTERS = dict()
maxLength = 12


Expand Down Expand Up @@ -114,12 +112,13 @@ def save(self):
scanEchoes = ConfigItem("Scanner", "ScanEchoes", False, BoolValidator())
scanDevItems = ConfigItem("Scanner", "ScanDevItems", False, BoolValidator())
scanResources = ConfigItem("Scanner", "ScanResources", False, BoolValidator())
scanAchievements = ConfigItem("Scanner", "scanAchievements", False, BoolValidator())

# TControlPanel settings
echoMinRarity = ConfigItem("Scanner", "EchoMinRarity", 1, RangeValidator(1, 5))
echoMinLevel = ConfigItem("Scanner", "EchoMinLevel", 0, RangeValidator(0, 90))
echoMinLevel = ConfigItem("Scanner", "EchoMinLevel", 0, RangeValidator(0, 25))
weaponsMinRarity = ConfigItem("Scanner", "WeaponsMinRarity", 1, RangeValidator(1, 5))
weaponsMinLevel = ConfigItem("Scanner", "WeaponsMinLevel", 0, RangeValidator(0, 90))
weaponsMinLevel = ConfigItem("Scanner", "WeaponsMinLevel", 1, RangeValidator(1, 90))

# Application metadata
HELP_URL = "https://t.me/psycho_marcus"
Expand Down
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
pyuac
PySide6-Fluent-Widgets
pytesseract
pydirectinput-rgx
mss
opencv-python
pygetwindow
pyinstaller
pyinstaller
pyperclip
90 changes: 90 additions & 0 deletions scraping/achievementsScraper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import string
import pyperclip
import pytesseract
import numpy as np

from scraping.utils import achievementsID
from scraping.utils import (
scaleWidth, scaleHeight, screenshot,
leftClick, presskey, hotkey
)
from properties.config import cfg

class Coordinates:
def __init__(self, x: int, y: int, w: int, h: int):
self.x = x
self.y = y
self.w = w
self.h = h

class ROI_Info:
def __init__(self, width: int, height: int, coords: dict[str, Coordinates]):
self.width = width
self.height = height
self.coords = coords

def scaleCoordinates(coords: dict[str, tuple[int, int, int, int]], width: int, height: int) -> dict[str, Coordinates]:
return {
key: Coordinates(
scaleWidth(x, width),
scaleHeight(y, height),
scaleWidth(w, width),
scaleHeight(h, height)
)
for key, (x, y, w, h) in coords.items()
}

def getROI(width: int, height: int) -> ROI_Info:
unscaled_coords = {
'status': (1579, 230, 256, 65),
'notFound': (855, 550, 280, 35),
'searchBar': (388, 149, 1, 1),
'searchButton': (629, 149, 1, 1),
'achievementsButton': (1674, 790, 1, 1),
'achievementsTab': (835, 570, 1, 1),
}
return ROI_Info(width, height, scaleCoordinates(unscaled_coords, width, height))

def processAchievement(image: np.ndarray, roiInfo: ROI_Info, achievementName: str) -> str | None:
coords = roiInfo.coords
notFoundText = pytesseract.image_to_string(
image[coords['notFound'].y:coords['notFound'].y + coords['notFound'].h,
coords['notFound'].x:coords['notFound'].x + coords['notFound'].w],
config='--psm 7'
).translate(str.maketrans('', '', string.digits + string.punctuation)).strip().lower()

if notFoundText != 'no search result':
statusText = pytesseract.image_to_string(
image[coords['status'].y:coords['status'].y + coords['status'].h,
coords['status'].x:coords['status'].x + coords['status'].w],
config=f'--psm 7 -c tessedit_char_whitelist={string.ascii_letters}'
).strip().lower()

if statusText != 'ongoing':
return achievementsID[achievementName]

return None

def achievementScraper(WIDTH: int, HEIGHT: int) -> list[str]:
achievements = []
roiInfo = getROI(WIDTH, HEIGHT)

presskey('esc', 1)
leftClick(roiInfo.coords['achievementsButton'].x, roiInfo.coords['achievementsButton'].y, 1.2)
leftClick(roiInfo.coords['achievementsTab'].x, roiInfo.coords['achievementsTab'].y, 1)

for achievementName in achievementsID:
pyperclip.copy(achievementName)
leftClick(roiInfo.coords['searchBar'].x, roiInfo.coords['searchBar'].y, .3)
hotkey('ctrl', 'v', waitTime=.3)
leftClick(roiInfo.coords['searchButton'].x, roiInfo.coords['searchButton'].y, .3)

image = screenshot(width=WIDTH, height=HEIGHT)
achievement = processAchievement(image, roiInfo, achievementName)
if achievement:
achievements.append(achievement)

leftClick(roiInfo.coords['searchButton'].x, roiInfo.coords['searchButton'].y, .3)

presskey('esc', .5)
return achievements
Loading

0 comments on commit e213f26

Please sign in to comment.