diff --git a/plugins/game_reader_test/plugin.py b/plugins/game_reader_test/plugin.py index cdd48b8..c47532f 100644 --- a/plugins/game_reader_test/plugin.py +++ b/plugins/game_reader_test/plugin.py @@ -674,14 +674,16 @@ class GameReaderTestPlugin(BasePlugin): """Parse skills from OCR text.""" skills = {} - # Ranks in Entropia Universe - RANKS = [ + # Ranks in Entropia Universe - multi-word first for proper matching + SINGLE_RANKS = [ 'Newbie', 'Inept', 'Beginner', 'Amateur', 'Average', - 'Skilled', 'Expert', 'Professional', 'Master', 'Grand Master', + 'Skilled', 'Expert', 'Professional', 'Master', 'Champion', 'Legendary', 'Guru', 'Astonishing', 'Remarkable', 'Outstanding', 'Marvelous', 'Prodigious', 'Amazing', 'Incredible', 'Awesome' ] - rank_pattern = '|'.join(RANKS) + MULTI_RANKS = ['Arch Master', 'Grand Master'] + ALL_RANKS = MULTI_RANKS + SINGLE_RANKS + rank_pattern = '|'.join(ALL_RANKS) # Clean text text = text.replace('SKILLS', '').replace('ALL CATEGORIES', '') @@ -705,6 +707,9 @@ class GameReaderTestPlugin(BasePlugin): rank = match.group(2) points = int(match.group(3)) + # Clean skill name - remove "Skill" prefix + skill_name = re.sub(r'^(Skill|SKILL)\s*', '', skill_name, flags=re.IGNORECASE) + if points > 0 and skill_name: skills[skill_name] = {'rank': rank, 'points': points} diff --git a/plugins/skill_scanner/plugin.py b/plugins/skill_scanner/plugin.py index f1401ac..7ef8909 100644 --- a/plugins/skill_scanner/plugin.py +++ b/plugins/skill_scanner/plugin.py @@ -54,26 +54,26 @@ class SkillOCRThread(QThread): """Parse skill data from OCR text with improved handling for 3-column layout.""" skills = {} - # Ranks in Entropia Universe (in order) - RANKS = [ + # Ranks in Entropia Universe (including multi-word ranks) + # Single word ranks + SINGLE_RANKS = [ 'Newbie', 'Inept', 'Beginner', 'Amateur', 'Average', - 'Skilled', 'Expert', 'Professional', 'Master', 'Grand Master', + 'Skilled', 'Expert', 'Professional', 'Master', 'Champion', 'Legendary', 'Guru', 'Astonishing', 'Remarkable', 'Outstanding', 'Marvelous', 'Prodigious', 'Amazing', 'Incredible', 'Awesome' ] - rank_pattern = '|'.join(RANKS) + # Multi-word ranks (must be checked first - longer matches first) + MULTI_RANKS = [ + 'Arch Master', 'Grand Master' + ] + + # Combine: multi-word first (so they match before single word), then single + ALL_RANKS = MULTI_RANKS + SINGLE_RANKS + rank_pattern = '|'.join(ALL_RANKS) # Clean up the text - remove common headers and junk text = text.replace('SKILLS', '').replace('ALL CATEGORIES', '') text = text.replace('SKILL NAME', '').replace('RANK', '').replace('POINTS', '') - text = text.replace('Attributes', '').replace('COMBAT', '').replace('Design', '') - text = text.replace('Construction', '').replace('Defense', '').replace('General', '') - text = text.replace('Handgun', '').replace('Heavy Melee Weapons', '') - text = text.replace('Information', '').replace('Inflict Melee Damage', '') - text = text.replace('Inflict Ranged Damage', '').replace('Light Melee Weapons', '') - text = text.replace('Longblades', '').replace('Medical', '').replace('Mining', '') - text = text.replace('Science', '').replace('Social', '').replace('Beauty', '') - text = text.replace('Mindforce', '') lines = text.split('\n') @@ -88,6 +88,7 @@ class SkillOCRThread(QThread): # Try pattern: SkillName Rank Points # More flexible pattern to handle merged text + # Skill name can be 2-50 chars, rank from our list, points 1-6 digits match = re.search( rf'([A-Za-z][A-Za-z\s]{{2,50}}?)\s+({rank_pattern})\s+(\d{{1,6}})(?:\s|$)', line, re.IGNORECASE @@ -98,11 +99,12 @@ class SkillOCRThread(QThread): rank = match.group(2) points = int(match.group(3)) - # Clean up skill name + # Clean up skill name - remove common words that might be prepended + skill_name = re.sub(r'^(Skill|SKILL)\s*', '', skill_name, flags=re.IGNORECASE) skill_name = skill_name.strip() # Validate - points should be reasonable (not too small) - if points > 0: + if points > 0 and skill_name: skills[skill_name] = { 'rank': rank, 'points': points, @@ -112,7 +114,7 @@ class SkillOCRThread(QThread): # Alternative parsing: try to find skill-rank-points triplets if not skills: - skills = self._parse_skills_alternative(text, RANKS) + skills = self._parse_skills_alternative(text, ALL_RANKS) return skills @@ -123,13 +125,16 @@ class SkillOCRThread(QThread): # Find all rank positions in the text for rank in ranks: # Look for pattern: [text] [Rank] [number] - pattern = rf'([A-Z][a-z]{{2,}}(?:\s+[A-Z][a-z]{{2,}}){{0,3}})\s+{rank}\s+(\d{{1,6}})' + pattern = rf'([A-Z][a-z]{{2,}}(?:\s+[A-Z][a-z]{{2,}}){{0,3}})\s+{re.escape(rank)}\s+(\d{{1,6}})' matches = re.finditer(pattern, text, re.IGNORECASE) for match in matches: skill_name = match.group(1).strip() points = int(match.group(2)) + # Clean skill name + skill_name = re.sub(r'^(Skill|SKILL)\s*', '', skill_name, flags=re.IGNORECASE) + if points > 0 and len(skill_name) > 2: skills[skill_name] = { 'rank': rank, @@ -216,6 +221,9 @@ class SkillScannerPlugin(BasePlugin): scan_group = QGroupBox("OCR Scan (Core Service)") scan_layout = QVBoxLayout(scan_group) + # Buttons row + buttons_layout = QHBoxLayout() + scan_btn = QPushButton("Scan Skills Window") scan_btn.setStyleSheet(""" QPushButton { @@ -228,7 +236,23 @@ class SkillScannerPlugin(BasePlugin): } """) scan_btn.clicked.connect(self._scan_skills) - scan_layout.addWidget(scan_btn) + buttons_layout.addWidget(scan_btn) + + reset_btn = QPushButton("Reset Data") + reset_btn.setStyleSheet(""" + QPushButton { + background-color: #ff4757; + color: white; + padding: 12px; + border: none; + border-radius: 4px; + font-weight: bold; + } + """) + reset_btn.clicked.connect(self._reset_data) + buttons_layout.addWidget(reset_btn) + + scan_layout.addLayout(buttons_layout) self.scan_progress = QLabel("Ready to scan") self.scan_progress.setStyleSheet("color: rgba(255,255,255,150);") @@ -312,8 +336,30 @@ class SkillScannerPlugin(BasePlugin): self._refresh_skills_table() self.scan_progress.setText(f"Found {len(skills_data)} skills") + def _reset_data(self): + """Reset all skill data.""" + from PyQt6.QtWidgets import QMessageBox + + reply = QMessageBox.question( + None, + "Reset Skill Data", + "Are you sure you want to clear all scanned skill data?\n\nThis cannot be undone.", + QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, + QMessageBox.StandardButton.No + ) + + if reply == QMessageBox.StandardButton.Yes: + self.skills_data = {} + self.skill_gains = [] + self._save_data() + self._refresh_skills_table() + self.gains_text.clear() + self.total_gains_label.setText("Total gains: 0") + self.scan_progress.setText("Data cleared") + def _on_scan_error(self, error): self.scan_progress.setText(f"Error: {error}") + self.scan_progress.setText(f"Error: {error}") def _refresh_skills_table(self): self.skills_table.setRowCount(len(self.skills_data))