diff --git a/borealis_overlay.py b/borealis_overlay.py index 14eb360..9f8e755 100644 --- a/borealis_overlay.py +++ b/borealis_overlay.py @@ -16,10 +16,10 @@ from rich.console import Console from rich.table import Table from rich.progress import Progress, BarColumn, TextColumn -# ---------------- [ Global Config ] ---------------- +# ---- [ Global Config ] ---- pytesseract.pytesseract.tesseract_cmd = r"C:\Program Files\Tesseract-OCR\tesseract.exe" -OCR_ENGINE = "Tesseract" # Hard-coded since we removed EasyOCR +OCR_ENGINE = "Tesseract" POLLING_RATE_MS = 1000 MAX_DATA_POINTS = 7 @@ -60,9 +60,7 @@ def format_experience_value(value): def format_duration(seconds): """ - Convert total seconds into hours / min / sec: - - If hours > 0 => "Xh Ym Zs" - - Otherwise => "Xm Ys" + Convert total seconds into hours/min/seconds (e.g. "Xh Ym Zs" or "Xm Ys"). """ seconds = int(seconds) hours = seconds // 3600 @@ -133,13 +131,13 @@ class OverlayCanvas(QWidget): if event.button() == Qt.LeftButton: for region in reversed(self.regions): - # Check handles + # Check each handle for i, handle in enumerate(region.resize_handles()): if handle.contains(event.pos()): self.selected_region = region self.selected_handle = i return - # Check label or rect + # Check label or main rect if region.label_rect().contains(event.pos()): self.selected_region = region self.selected_handle = None @@ -156,7 +154,7 @@ class OverlayCanvas(QWidget): return if self.selected_handle is None: - # Drag rectangle + # Drag entire rectangle self.selected_region.x = event.x() - self.drag_offset.x() self.selected_region.y = event.y() - self.drag_offset.y() else: @@ -191,33 +189,29 @@ class OverlayCanvas(QWidget): self.selected_handle = None class BorealisOverlay(QWidget): - """ - Project Borealis with Tesseract + Rich table + Rich progress bar, - properly displayed with "with Progress(...) as progress". - """ def __init__(self): super().__init__() - # Fullscreen overlay screen_geometry = QApplication.primaryScreen().geometry() self.setGeometry(screen_geometry) self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) self.setAttribute(Qt.WA_TranslucentBackground, True) - self.regions = [ - Region(250, 50, label="Experience"), - ] + self.regions = [ Region(250, 50, label="Experience") ] self.canvas = OverlayCanvas(self.regions, self) self.canvas.setGeometry(self.rect()) - # Tesseract + # Tesseract only self.engine = pytesseract - self.points = [] + self.timer = QTimer(self) self.timer.timeout.connect(self.collect_ocr_data) self.timer.start(POLLING_RATE_MS) + # --------------------------------------------------------------------- + # OCR & Data + # --------------------------------------------------------------------- def collect_ocr_data(self): if not self.engine: return @@ -227,9 +221,9 @@ class BorealisOverlay(QWidget): screenshot = ImageGrab.grab( bbox=(region.x, region.y, region.x + region.w, region.y + region.h) ) - processed_image = self.preprocess_image(screenshot) + processed = self.preprocess_image(screenshot) - text = pytesseract.image_to_string(processed_image, config='--psm 6 --oem 1') + text = pytesseract.image_to_string(processed, config='--psm 6 --oem 1') region.data = text.strip() if region.label.lower() == "experience": @@ -255,16 +249,20 @@ class BorealisOverlay(QWidget): now = time.time() if self.points: _, last_v = self.points[-1] + # skip duplicates if abs(new_val - last_v) < 1e-6: return + # rollover if new_val < last_v: self.points.clear() self.points.append((now, new_val)) if len(self.points) > MAX_DATA_POINTS: self.points.pop(0) + # --------------------------------------------------------------------- + # Table & Calculation + # --------------------------------------------------------------------- def compute_time_to_100(self): - """Return integer seconds until 100% or None if not feasible.""" n = len(self.points) if n < 2: return None @@ -292,6 +290,8 @@ class BorealisOverlay(QWidget): return int(remain / rate_per_s) def display_ocr_data_in_terminal(self): + from rich.progress import Progress, BarColumn, TextColumn + console = Console() os.system('cls' if os.name == 'nt' else 'clear') @@ -299,9 +299,9 @@ class BorealisOverlay(QWidget): console.print("[dim]Flyff Information Overlay[/dim]\n") console.print(f"[bold]OCR Engine[/bold]: {OCR_ENGINE}") - console.print(f"[bold]Data Polling Rate[/bold]: {POLLING_RATE_MS / 1000}s\n") + console.print(f"[bold]Data Polling Rate[/bold]: {POLLING_RATE_MS/1000}s\n") - # Build the historical table + # Build table table = Table(show_header=True, header_style=GREEN_HEADER_STYLE, style=None) table.add_column("Historical EXP", justify="center", style="green") table.add_column("Time Since Last Kill", justify="center", style="green") @@ -309,6 +309,7 @@ class BorealisOverlay(QWidget): table.add_column("Average Time Between Kills", justify="center", style="green") n = len(self.points) + # If we only have 1 data point => show single row if n == 1: t0, v0 = self.points[0] exp_str = f"[green]{format_experience_value(v0)}%[/green]" @@ -318,30 +319,46 @@ class BorealisOverlay(QWidget): t_cur, v_cur = self.points[i] t_prev, v_prev = self.points[i - 1] - exp_str = f"[green]{format_experience_value(v_cur)}%[/green]" + # Calculate a difference + delta_v = v_cur - v_prev + delta_str = f"{delta_v:+.4f}%" + # e.g. "v_cur + (delta in dark gray)" + # "72.8260% [dim](+0.0334%) [/dim]" + exp_main = format_experience_value(v_cur) + exp_str = ( + f"[green]{exp_main}%[/green] " + f"[dim]({delta_str})[/dim]" + ) + + # Time since last kill delta_t = t_cur - t_prev t_since_str = f"{delta_t:.1f}s" + # average exp from first data point diff_v = v_cur - self.points[0][1] steps = i avg_exp_str = f"{diff_v/steps:.4f}%" + # average time from first data point total_time = t_cur - self.points[0][0] avg_kill_time = total_time / steps avg_time_str = f"{avg_kill_time:.1f}s" - table.add_row(exp_str, t_since_str, avg_exp_str, avg_time_str) + table.add_row( + exp_str, + t_since_str, + avg_exp_str, + avg_time_str + ) console.print(table) console.print() - # Prepare the progress bar + # Progress bar current_exp = self.points[-1][1] if self.points else 0.0 secs_left = self.compute_time_to_100() - time_str = format_duration(secs_left) if secs_left is not None else "???" + time_str = format_duration(secs_left) if secs_left else "???" - # We'll display a single progress bar line in a context manager - # Setting transient=False ensures it remains in the console with Progress( TextColumn("[bold white]Predicted Time to Level:[/bold white] "), BarColumn(bar_width=30), @@ -351,16 +368,14 @@ class BorealisOverlay(QWidget): transient=False, ) as progress: task_id = progress.add_task("", total=100, completed=current_exp) - # Force a manual refresh so the bar appears immediately progress.refresh() def main(): app = QApplication(sys.argv) window = BorealisOverlay() - window.setWindowTitle("Project Borealis Overlay (Fixed Progress Bar)") + window.setWindowTitle("Project Borealis Overlay (Delta in Historical EXP)") window.show() sys.exit(app.exec_()) - if __name__ == "__main__": main()