#!/usr/bin/env python3 """ Flyff Character Status Node (New Version): - Has no inputs/outputs. - Creates an OCR region in data_collector. - Periodically grabs raw text from that region, parses it here in the node, and sets data_manager's HP, MP, FP, EXP accordingly. - Also updates its own text fields with the parsed values. """ import re from OdenGraphQt import BaseNode from PyQt5.QtWidgets import QMessageBox from Modules import data_manager, data_collector class FlyffCharacterStatusNode(BaseNode): __identifier__ = 'bunny-lab.io.flyff_character_status_node' NODE_NAME = 'Flyff - Character Status' def __init__(self): super(FlyffCharacterStatusNode, self).__init__() # Prevent duplicates if data_manager.character_status_collector_exists: QMessageBox.critical(None, "Error", "Only one Flyff Character Status Collector node is allowed.") raise Exception("Duplicate Character Status Node.") data_manager.character_status_collector_exists = True # Add text fields for display self.add_text_input('hp', 'HP', text="HP: 0/0") self.add_text_input('mp', 'MP', text="MP: 0/0") self.add_text_input('fp', 'FP', text="FP: 0/0") self.add_text_input('exp', 'EXP', text="EXP: 0%") # Create a unique region id for this node (or just "character_status") self.region_id = "character_status" data_collector.create_ocr_region(self.region_id, x=250, y=50, w=180, h=130) # Start the data_collector background thread (if not already started) data_collector.start_collector() # Set the node name self.set_name("Flyff - Character Status") def parse_character_stats(self, raw_text): """ Extract HP, MP, FP, EXP from the raw OCR text lines. """ lines = [l.strip() for l in raw_text.splitlines() if l.strip()] hp_current, hp_total = 0, 0 mp_current, mp_total = 0, 0 fp_current, fp_total = 0, 0 exp_value = 0.0 if len(lines) >= 4: # line 1: HP hp_match = re.search(r"(\d+)\s*/\s*(\d+)", lines[0]) if hp_match: hp_current = int(hp_match.group(1)) hp_total = int(hp_match.group(2)) # line 2: MP mp_match = re.search(r"(\d+)\s*/\s*(\d+)", lines[1]) if mp_match: mp_current = int(mp_match.group(1)) mp_total = int(mp_match.group(2)) # line 3: FP fp_match = re.search(r"(\d+)\s*/\s*(\d+)", lines[2]) if fp_match: fp_current = int(fp_match.group(1)) fp_total = int(fp_match.group(2)) # line 4: EXP exp_match = re.search(r"(\d+(?:\.\d+)?)", lines[3]) if exp_match: val = float(exp_match.group(1)) if val < 0: val = 0 if val > 100: val = 100 exp_value = val return hp_current, hp_total, mp_current, mp_total, fp_current, fp_total, exp_value def process_input(self): """ Called periodically by the global timer in your main application (borealis.py). """ # Grab raw text from data_collector raw_text = data_collector.get_raw_text(self.region_id) # Parse it hp_c, hp_t, mp_c, mp_t, fp_c, fp_t, exp_v = self.parse_character_stats(raw_text) # Update data_manager data_manager.set_data_bulk({ "hp_current": hp_c, "hp_total": hp_t, "mp_current": mp_c, "mp_total": mp_t, "fp_current": fp_c, "fp_total": fp_t, "exp": exp_v }) # Update the node's text fields self.set_property('hp', f"HP: {hp_c}/{hp_t}") self.set_property('mp', f"MP: {mp_c}/{mp_t}") self.set_property('fp', f"FP: {fp_c}/{fp_t}") self.set_property('exp', f"EXP: {exp_v}%")