Skip to content

Commit

Permalink
v1.6.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Psycho-Marcus committed Oct 28, 2024
1 parent 4018264 commit 7be934c
Show file tree
Hide file tree
Showing 22 changed files with 640 additions and 366 deletions.
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ The data format is specifically designed for [WuWa Tracker](https://wuwatracker.
*Note: This tool currently works __only__ in full-screen mode.*

- **Supported Screens:**
- 1680x1050
- 1920x1080
- 2560x1440
- (other resolutions not tested; may not be compatible)

- **Supported Languages:**
- English
- All (tested only with English)

- **Features:**
- Scan Characters
Expand All @@ -26,12 +27,12 @@ The data format is specifically designed for [WuWa Tracker](https://wuwatracker.
## To-Do List
- [x] Character Scanner (no echo)
- [x] Weapons Scanner
- [x] Echoes Scanner (really slow at the moment)
- [x] Echoes Scanner
- [x] Achievements Scanner
- [ ] Auto Updater
- [ ] Support for additional in-game languages
- [x] Support for additional in-game languages
- [ ] Support for more software languages
- [ ] Improve text recognition accuracy
- [x] Improve text recognition accuracy
- [ ] Improve logs
- [ ] Optimize releases size
- [ ] Rewrite the code (after all tasks are complete; this is urgently needed)
Expand Down
2 changes: 1 addition & 1 deletion game/foreground.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def setForeground(self):
pPATH = self.process_utils.getPPATH(pID)
pName = self.process_utils.getPNAME(pPATH) if pPATH else None

logger.debug(f"Window handle: {hwnd}, PID: {pID}, Path: {pPATH}, Name: {pName}")
logger.debug(f"Window handle: {hwnd}, PID: {pID}, Name: {pName}")

if pName == PROCESS_NAME:
WindowFocusManager.PROCESS_ID = pID
Expand Down
19 changes: 9 additions & 10 deletions game/menu.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import time
import logging
from difflib import get_close_matches

from game.screenSize import WindowManager
from game.foreground import WindowFocusManager
from scraping.utils.common import definedText
from scraping.utils import (
scaleWidth, scaleHeight, screenshot,
imageToString
screenshot, imageToString
)

logger = logging.getLogger('MainMenuController')
Expand All @@ -21,17 +22,18 @@ def isMenu(self) -> bool:
bool: True if the main menu is detected, False otherwise.
"""
try:
screenInfo = WindowManager.getScreenInfo()
image = screenshot(
scaleWidth(140, WindowManager.getWidth()),
scaleHeight(40, WindowManager.getHeight()),
scaleWidth(150, WindowManager.getWidth()),
scaleHeight(40, WindowManager.getHeight())
screenInfo.scaleWidth((140, 132)),
screenInfo.scaleHeight((40, 32)),
screenInfo.scaleWidth(150),
screenInfo.scaleHeight(40)
)

result = imageToString(image, '').lower()
logger.debug(f"Detected text from screenshot: '{result}'")

return result == 'terminal' # MULTILANG
return 'terminal' if get_close_matches(result, [definedText['PrefabTextItem_1547656443_Text']]) else 'none' # MULTILANG
except Exception as e:
logger.error(f"Failed to capture or process screenshot: {e}")
return False
Expand All @@ -51,9 +53,6 @@ def isInMainMenu(self):
if result[0] == 'error':
return result
time.sleep(.2)
WindowManager.updateWindowSize()

time.sleep(1)

if not self.isMenu():
return 'error', 'Error', 'Not in the main menu. Press ESC in-game and rerun the scanner.'
Expand Down
34 changes: 34 additions & 0 deletions game/screenInfo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import math
from dataclasses import dataclass

@dataclass
class ScreenInfo:
width: int
height: int

@property
def ratio(self):
gcd = math.gcd(self.width, self.height)
return (self.width // gcd, self.height // gcd)

def scaleWidth(self, value: int|float|tuple):
return self._scale(value, self.width, 1920, 1680)

def scaleHeight(self, value: int|float|tuple):
return self._scale(value, self.height, 1080, 1050)

def _scale(self, value, dimension, base16_9, base8_5):
if isinstance(value, tuple):
if len(value) == 2:
v16_9, v8_5 = value
elif len(value) == 3:
v16_9 = value[0] + (value[2][0] if isinstance(value[2], tuple) else value[2])
v8_5 = value[1] + (value[2][1] if isinstance(value[2], tuple) else value[2])
else:
v16_9 = v8_5 = value

if self.ratio == (8, 5):
if not isinstance(value, tuple): base8_5 = base16_9
return int(v8_5 / base8_5 * dimension)

return int(v16_9 / base16_9 * dimension)
50 changes: 21 additions & 29 deletions game/screenSize.py
Original file line number Diff line number Diff line change
@@ -1,39 +1,31 @@
import logging
import pygetwindow as gw

from game.screenInfo import ScreenInfo
from properties.config import DPI_SCALING

logger = logging.getLogger('WindowManager')

class WindowManager:
_width = 1920
_height = 1080

@classmethod
def getWindowSize(cls):
"""Update and return the size of the active window, adjusted for DPI scaling."""
try:
window = gw.getActiveWindow()
if window is None:
raise ValueError("No active window found.")
cls._width = int(window.width / DPI_SCALING)
cls._height = int(window.height / DPI_SCALING)
return cls._width, cls._height
except Exception as e:
logger.error(f"Error getting window size: {e}")
return cls._width, cls._height

@classmethod
def getWidth(cls):
"""Return the width of the active window."""
return cls._width
class WindowManager:
_screen_info = ScreenInfo(1920, 1080)

@classmethod
def getHeight(cls):
"""Return the height of the active window."""
return cls._height
@classmethod
def getWindowSize(cls):
try:
window = gw.getActiveWindow()
if window is None:
raise ValueError("No active window found.")
cls._screen_info = ScreenInfo(
int(window.width / DPI_SCALING),
int(window.height / DPI_SCALING)
)
return cls._screen_info
except Exception as e:
logger.error(f"Error getting window size: {e}")
return cls._screen_info

@classmethod
def updateWindowSize(cls):
"""Update the stored window size."""
cls.getWindowSize()
@classmethod
def getScreenInfo(cls):
cls.getWindowSize()
return cls._screen_info
4 changes: 2 additions & 2 deletions main.spec
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ from PyInstaller.utils.win32 import versioninfo as vi
from pathlib import Path
import importlib.util

version = (1, 5, 4, 0)
version = (1, 6, 0, 0)

version_file = vi.VSVersionInfo(
ffi=vi.FixedFileInfo(
Expand Down Expand Up @@ -72,7 +72,7 @@ a = Analysis(
('assets', 'assets'),
*add_data
],
hiddenimports=[],
hiddenimports=['babel.dates', 'babel.numbers'],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
Expand Down
8 changes: 5 additions & 3 deletions properties/config.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import sys
import os
import sys
import json
import string
import ctypes

Expand All @@ -22,7 +23,8 @@
INVENTORY = {'date': str(), 'items': dict()}
FAILED: list[dict] = list()
maxLength = 12

try: LANGUAGES = json.load(open(os.path.join(os.path.dirname(basePATH), 'data', 'languages.json'), 'r', encoding='utf-8'))
except: LANGUAGES = {'English': 'en'}

def alphabethList() -> list[str]:
"""Generate a list of uppercase letters, digits, and punctuation."""
Expand Down Expand Up @@ -100,7 +102,7 @@ def save(self):
# Configuration items
exportFolder = ConfigItem("Folders", "Export", "export", FolderValidator())
checkUpdateAtStartUp = ConfigItem("Update", "CheckUpdateAtStartUp", True, BoolValidator())
gameLanguages = OptionsConfigItem('InGame', 'Language', 'English', OptionsValidator(['English']))
gameLanguage = OptionsConfigItem('InGame', 'Language', 'English', OptionsValidator(list(LANGUAGES)))
inventoryKeybind = OptionsConfigItem('InGame', 'InventoryKeybind', 'B', OptionsValidator(alphabethList()))
resonatorKeybind = OptionsConfigItem('InGame', 'ResonatorKeybind', 'C', OptionsValidator(alphabethList()))
roverName = ConfigItem('InGame', 'RoverName', 'Rover', TextValidator(max_length=maxLength))
Expand Down
17 changes: 9 additions & 8 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
pyuac
PySide6-Fluent-Widgets
rapidocr-onnxruntime
mss
opencv-python
pygetwindow
pyinstaller
pyperclip
pyuac==0.0.3
PySide6-Fluent-Widgets==1.6.5
rapidocr-onnxruntime==1.3.24
mss==9.0.2
opencv-python==4.10.0.84
pygetwindow==0.0.9
pyinstaller==6.10.0
pyperclip==1.9.0
babel==2.16.0
39 changes: 19 additions & 20 deletions scraping/achievementsScraper.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import pyperclip
import numpy as np

from scraping.utils import achievementsID
from scraping.utils import achievementsID, definedText
from scraping.utils import (
scaleWidth, scaleHeight, screenshot,
imageToString, convertToBlackWhite, leftClick,
presskey, hotkey
screenshot, imageToString, convertToBlackWhite,
leftClick, presskey, hotkey
)
from game.screenInfo import ScreenInfo

class Coordinates:
def __init__(self, x: int, y: int, w: int, h: int):
Expand All @@ -21,27 +21,26 @@ def __init__(self, width: int, height: int, coords: dict[str, Coordinates]):
self.height = height
self.coords = coords

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

def getROI(width: int, height: int) -> ROI_Info:
def getROI(screenInfo: ScreenInfo) -> 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),
'status': (1579, (230, 197), 256, 65),
'searchBar': (388, (149, 129), 1, 1),
'searchButton': (629, (149, 129), 1, 1),
'achievementsButton': (1674, (790, 690), 1, 1),
'achievementsTab': (835, 570, 1, 1),
}
return ROI_Info(width, height, scaleCoordinates(unscaled_coords, width, height))
return ROI_Info(screenInfo.width, screenInfo.height, scaleCoordinates(unscaled_coords, screenInfo))

def processAchievement(image: np.ndarray, roiInfo: ROI_Info, achievementName: str, _cache: dict) -> str | None:
coords = roiInfo.coords
Expand All @@ -53,15 +52,15 @@ def processAchievement(image: np.ndarray, roiInfo: ROI_Info, achievementName: st
statusText = imageToString(statusImage).lower()
_cache[statusHash] = statusText

if statusText == 'claim' or '/' in statusText: # MULTILANG
if statusText == definedText['PrefabTextItem_128820487_Text'] or '/' in statusText: # MULTILANG
return achievementsID[achievementName]

return None

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

presskey('esc', 1)
leftClick(roiInfo.coords['achievementsButton'].x, roiInfo.coords['achievementsButton'].y, 1.2)
Expand All @@ -73,7 +72,7 @@ def achievementScraper(WIDTH: int, HEIGHT: int) -> list[str]:
hotkey('ctrl', 'v', waitTime=.3)
leftClick(roiInfo.coords['searchButton'].x, roiInfo.coords['searchButton'].y, .6)

image = screenshot(width=WIDTH, height=HEIGHT)
image = screenshot(width=screenInfo.width, height=screenInfo.height)
achievement = processAchievement(image, roiInfo, achievementName, _cache)
if achievement:
achievements.append(achievement)
Expand Down
Loading

0 comments on commit 7be934c

Please sign in to comment.