Borealis-Legacy/Data/Nodes/Flyff/flyff_character_status_node.py

125 lines
4.5 KiB
Python

#!/usr/bin/env python3
"""
Flyff Character Status Node:
- Creates an OCR region in data_collector.
- Periodically grabs raw text from that region and updates status.
- ALSO: Captures a screenshot from the same region, converts to base64,
and updates data_manager so the Flask server can serve it.
"""
import re
import base64
from io import BytesIO
from OdenGraphQt import BaseNode
from PyQt5.QtWidgets import QMessageBox
from PyQt5.QtCore import QTimer # Corrected import
# Import the existing modules
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__()
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
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%")
self.region_id = "character_status"
data_collector.create_ocr_region(
self.region_id, x=250, y=50, w=180, h=130,
color=(255, 255, 0), thickness=2
)
data_collector.start_collector()
self.set_name("Flyff - Character Status")
# Set up a timer to periodically update character stats
self.timer = QTimer()
self.timer.timeout.connect(self.process_input)
self.timer.start(1000) # Update every second
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 to update character status from OCR,
and also capture the screenshot to display via Flask.
"""
# 1) Update the text-based status (same as before).
raw_text = data_collector.get_raw_text(self.region_id)
hp_c, hp_t, mp_c, mp_t, fp_c, fp_t, exp_v = self.parse_character_stats(raw_text)
# Update data_manager with the parsed values
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 UI 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}%")
# 2) Capture the screenshot used by OCR and store as base64
screenshot_img = data_collector.capture_region_as_image(self.region_id)
if screenshot_img:
# Convert PIL image to base64
buf = BytesIO()
screenshot_img.save(buf, format='PNG')
image_b64 = base64.b64encode(buf.getvalue()).decode('utf-8')
data_manager.set_status_screenshot(image_b64)