From 76cb82acd973244074f15d280109c951705fb050 Mon Sep 17 00:00:00 2001 From: Nicole Rappe Date: Sun, 16 Feb 2025 22:50:39 -0700 Subject: [PATCH] Milestone --- .../data_collector.cpython-312.pyc | Bin 10299 -> 9878 bytes Modules/data_collector.py | 6 +- .../flyff_EXP_current.cpython-312.pyc | Bin 4652 -> 4472 bytes ...lyff_character_status_node.cpython-312.pyc | Bin 5251 -> 5132 bytes ...ff_leveling_predictor_node.cpython-312.pyc | Bin 0 -> 6684 bytes Nodes/Flyff/flyff_EXP_current.py | 2 - Nodes/Flyff/flyff_character_status_node.py | 4 +- Nodes/Flyff/flyff_leveling_predictor_node.py | 141 ++++++++++++++++++ borealis.py | 2 +- 9 files changed, 144 insertions(+), 11 deletions(-) create mode 100644 Nodes/Flyff/__pycache__/flyff_leveling_predictor_node.cpython-312.pyc create mode 100644 Nodes/Flyff/flyff_leveling_predictor_node.py diff --git a/Modules/__pycache__/data_collector.cpython-312.pyc b/Modules/__pycache__/data_collector.cpython-312.pyc index 2cfb3154669bcf66305291adf2b72ceea8d6040a..d93ef5815407ab047c4b5afb35e2ce319a191d31 100644 GIT binary patch delta 1742 zcmZWpZ){Ul6uWZ%ohE^C26Nep6DjJSjSs5GDpl@Y; zbU~9*j(5XHF$r#$YJX1na%t5CtR02oq69fwlfr z9pzBwWu@#Xt9f-macW@98;^Rfd7LHlGTVX2DhD(=fGN*<({Mm*_Glsn1?>cHgKxB0 zWDDB03%Dg;v%YiPfgU>(7Fk}6^YF0SfWFFH1b-_j!5 zND@F+Ga@^hA&uCEqOCAxq|t4y(g+WYL9Gd8q8UoQ<*Qq8YjT%6HkEY2LkKG%oJ_7% zC|EhCB*aeC?Sb3A&Q_v1hjVEdxRd1W!x*NR53cJbwEFYj&lr1|!+Uzy%t!tsJbdwv z%ADBZvB3uTIa(jrZZ0$uXOsNmN>fh@6HQDmOt1ng*HHTTlXCpJ+YDBDaMi*o+mURz z>S=4t7VF`Qz;4iktx@tIs0qX-f=3B9!_Hv5i`?0MgqEctDHGethM=1){(y<#Y~uSs z>VBL>n&*?u7rgAh;Gf)dG4$73RudA*nX}7|^4eB3Chmq7xr5(A8~F){hvP{0*6>h> z>LK4}AB(BNd_rtYFlQw`-`zm}MnXLKA2we7E@Lj6GJj&6x zxYkD}(y%Pr~6?D=$JRwi;XY9Yg_lV{dD+_8eY|Mqs?*%k33gIzT<()*RfM z?(Xf$96j_*kLeLbQdGFdolU7B=@q(%VZ1TLC*XP`cIC{UjgRvk!{oxqm5NRl9{|@g zRI){ZT%1P08z9|{kP$y6#&ES&1S+FwgOT`VejaYcPouYn#4p}gWfO%f4ys{NPq=!% zQ0mVXo|4V6`1Q2Np|09q@@iG@nyU>D4P+fl2IHz{N+XtOR70w!>3WOSXl4|~lWoY{ zyU9&_a%MKUTEU_}Ozmoa1%HC~wwjkxb+UAYp}&0_pN4Dg)I^`OuR7jOp}j;vB#Cnb zlLS`~++Ze?D`aguXB7&W4Bng)N@YXtsgO;bCS<#jf@=8BSvIc(ypg#C?CfZalSmqv zrjReg0Tj53u*3>@p(Db(;f;OQ zjLOB@PoFXjx-s&lmZ7=Ejx(i!;exeGybo`7itRc-#P8R!RObiD+rc}{4_KHt&82i6 eo~u*%vNF_^<7;=;V delta 2228 zcmZuyTWl0n7@jjbvopKhZM(hSJMC_ZV_Tpts9*yWC^w-BZL|=RVY@qBwrqE|XNHzG zYg!W$Q86?*C`Kfy51JBqSZHF_3rQb*K_4ukTrxqUNn^r`HU)`+82!)LZkHHOGT%9K z&j0`OpYOlSM+d%n&HaPh?c`wmbnRN`@2l==9^EHRd~-{mDbj5^g8XHaULnq0S#g|c zPw#QupiDZ;3mq#bev!1~)-;>UDis;b&j@DcpzVrqnNPnD7J_;O`JI=LNFMXuedA3% zrXQQ(Xh=EO8IMQ80lyiJMU`+=@hgTN_hY}QE9Nn*`-5wHFY%Tg#6!Ai*@J$4C>B+n znEGbP;izHyqXFF#<2W2O$uZkIY6F~9!DDm-OmdH$oLqEQs=6sv-I8kNrN;ZRdp`H& z1-XOd32P@LPF{n(Ao<`4g*Je|DlhQjhQ|ia!y>r-`DfS(q6BvVm`fl)TY_h3V<-R$ z7jvHBlv-fp^2!W63v@|#N?s3V$}8L42#1SJflN9Y^T)&lO7IC=LI?>#yXhuNLS?b^ zsVys<;5tArEiR0Ss!aBY-3{Z#JAgMv?yEZvDTnkBP`gnXHSpscGr z-T;#&nEFZ6pqZm6m^tNw$ZOa?s?ifnue^zTEp>xg4_{8|<-y%Gu!n1@cD{BZ9?aYz z5{t!g9c-(}LZ(5}cg18LqzyK;Kr9l`17-~4ZE&`o+>+OT)u(c~90Ui>a}JIq9V+Q^ z44@xpuQ(Fus96evYy|Br@>0Tafx9SVV}0hPz?tP4sX@x{e@0NwN>8CP>s3cLZUz|@ z;btxCOD12L?O^P82>02{w;RdcfhT0$gjn2bhCIIfQwxQ$%R*($Qo zlBJxwK580or0@cT7b(01U^xwlwjMQ(#Z1c)$FYEJ7*)mby2nQkC6jnickX8&^#7Gd)Q@*_!kw52aAjNY$qfrgqHdZd;H$f012h9q&3m$id#(yM^bFy_Y(4 zkPH{M+7Hv^5i(O;hla`b#ZAaZ9s`b$`jSt8%IhURXGA_LEvp`;(yOUpP(H!Av|}g9 zol+kWdj)lzgmX&{#G+4Iu|(r$w=BhivVkyyV!(#LW&^L7|hma;j|xXpGB%bu=z z=8Po_#YS{h&bpMnrX@!8QEimmQM|y;6Qv!Un_XMi!~;D?yjvRHhL1oO%HE658d+^f znDV+z!cLCfc_Q`{pyS1ejqTw=p=l_)xwb0x89xj+#c9`8iF zsIb2nQ&QCi%v$1JP_%fg<4U>^qbG*qV-_EcTeiSR8{SQR^gnC7cVOOx6NTBM683A9ZqC;pSY#5ZEfaL% zqKhu9mtvq)!EIfr0oQ6H7P_fWuoM@9e?WqQ;KnzNL$dHL&OP_Oa~|jMUZ#F0Tn8?f z16UP4y;+=j?GiZKz72!ik=LM_{dN{We<*=rt9tOUQ^B*mJmWXzW*PQ+pvwSuZC$3@ zmY`wRI0J4H>&$>^$l|@O@%V_3djw6iybZ1%!9zZj9_* z!=`fp-w9mqe>q%+z;d_^aT|8*{aLUhWbB^#HD&Xaf)_+Du8KK45N{AaW~4&vmJ}dl z9`8A(Vo@paH57YlY#N8D;(q)nG`Bu|tR%iD`Oivzy@KBySMjnu8%#nt(E;K+b37P* zQu;v|`}1=cU&xhWwxq)Q@jyw25A@O~8}{-l!?rA!BD5W}nLJEP9-$^zrE;9M(}l81 zKdM|g-pV<@lgLH(Vm;5`guAORE!RhQJz}oTVa7cXJkMy2XE1bHy7_%7EFrrW_mA(s)JmSHG4aN1Yl dQe2`lhfUuo8OJ@}bzg;$$!7K!m|a(y?k@$6h^hbp delta 890 zcmaJ<-%Aux6h3!mXLfeh*_|=nOijmKf0w3<)}}6H?xtmmsF5NO!Kk^F$=wlGnWcX~ zg1S8jVJ|&}L75QZlZHM-FB|Mbg%DBnQV$^oK@WAW+u#P#c{t}g_kQ2uJD2;z|EOGf zsVFkgc=~vG{L`}H6gc3?hUXAz7akrqz3hsG;js97u*fZ;Opn0g4dH4v0KVk{i;HRG zm8Wnpf#Mv32}nB3{0tGma+p3FFL4=!ImR(W3oiW`NO=%DVDt8>;UrhYvJ;Zwvb9Me z%a&{jf7>#PY^^2OW|JaGiN0LJZHe{}>K|gIBrOJRmf3>6YUHqO4P}wHM<6gf*=A=U z0A{YOC5qaa;*COBZmapJg4WeDtF0+tXIkwd^YU3=1r+#q0BI$!iw@q2 zeB~+n`K_9Kk-JYGjf7w$oENEr=URJ+Hk)!W%T?}3Grt++7;ZugH}edKT+uMUg@Vx# zzZDXq;guQp2eiMHzAQe%5_{ts5wl-~n@C(~^c|xxF4Cm$24S V2IAF*v>t>)3)SC&Ra{JYzX4F5w}1cu diff --git a/Nodes/Flyff/__pycache__/flyff_character_status_node.cpython-312.pyc b/Nodes/Flyff/__pycache__/flyff_character_status_node.cpython-312.pyc index 3192abdb659b64b895a81563a4a6881051ecb2b6..d98ebf349276b113c14be1755a0350290e73c735 100644 GIT binary patch delta 888 zcma))%}*0S6u{^0$LzMV({)=2mX8x5u|^{wasW~!sX|Q%;8jkB5o1w8mL4FP#;6A` z+#Uy#!XJQ~YOfqTX^dx2UOjOlAsUa)Y}wFFB<&{qW?$y_@|%5c-WT?6@$a0ciF!VM z{q$nJ!SA^+jYap=9IraKYz#gXmNbQ(SHy{-IYMeovhaSDwK75^tMK^yY8CVZT3Ni- ztYi(2vJum6*U*HwK z`KvGA{L2U6P6>{m7KaK>SW;A|+DXDkI?U^^phFpFgCaO6gFGx_J-7>V_$}C^Pt@l2 z4=iQ}>~l&cr<%{R7AT^UTcIQND*Rc(&$)h>!JoO;@Dd;Q6oOTNiG!WrM6cnPs{aQ} Cg^B_I delta 940 zcmaiz&ubGw6vuax{k=Q8*~Ex3tyHwym5P+KAk`M6q7@3pC?bk1Asa#xOmwr*C=*0` z@gVf@E`^%AQV_Hr#eYB$k(^4E{sl!Sdh+1RCb3xwbYR}heCN9#hWFmSoLI5(XN*;j zj^u~;rMK_!IT__(PX3sh_a!E?;qoN95$Wy`2an8Sr(A(VT`@(q{zXe8nm6s^u8re} zRxu%_j+^{{+KzfCzJa>7{@N1usZxll6Fo%LdsK0N%CwGsF>go75Av}0F`M& zsY;is&M2op=#^{!=8T?3XccbYytb`dU*q=cnT|dMKk*7= zjP~SJ#951|!D zUv_-z@W!d+U0@*#lh#>u3GP}o@g@}--{Fgu#@jM}QD19z^P9UCk5Cr&;@RV; wg?s3C4jmCP-W2;@p~2R-B*$^<1;_C&f}41ND)22~TlW#ltTlgeYz^Fh0rYyX1ONa4 diff --git a/Nodes/Flyff/__pycache__/flyff_leveling_predictor_node.cpython-312.pyc b/Nodes/Flyff/__pycache__/flyff_leveling_predictor_node.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c4f52993b93705f807cf6b209823be5d4f7c5b34 GIT binary patch literal 6684 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 a>c6A7@2D=QpILrR)4P{i{!QU85%oU= 4: - print("Processing OCR Lines:", lines) # Debugging output - # line 1: HP hp_match = re.search(r"(\d+)\s*/\s*(\d+)", lines[0]) if hp_match: @@ -85,7 +83,7 @@ class FlyffCharacterStatusNode(BaseNode): Called periodically to update character status from OCR. """ raw_text = data_collector.get_raw_text(self.region_id) - print("Raw OCR Text:", raw_text) # Debugging OCR text reading +# print("Raw OCR Text:", raw_text) # Debugging OCR text reading hp_c, hp_t, mp_c, mp_t, fp_c, fp_t, exp_v = self.parse_character_stats(raw_text) diff --git a/Nodes/Flyff/flyff_leveling_predictor_node.py b/Nodes/Flyff/flyff_leveling_predictor_node.py new file mode 100644 index 0000000..43e56a2 --- /dev/null +++ b/Nodes/Flyff/flyff_leveling_predictor_node.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python3 +""" +Flyff - Leveling Predictor Node: +- Tracks the last N changes in EXP values. +- Calculates the average change rate and time intervals. +- Predicts the estimated time to reach level 100. +""" + +import time +import numpy as np +from OdenGraphQt import BaseNode +from PyQt5.QtCore import QTimer +from Modules import data_manager + +class FlyffLevelingPredictorNode(BaseNode): + __identifier__ = "bunny-lab.io.flyff_leveling_predictor_node" + NODE_NAME = "Flyff - Leveling Predictor" + + def __init__(self): + super(FlyffLevelingPredictorNode, self).__init__() + + # Input port for EXP values + self.add_input("exp", "EXP") + + # User-defined number of changes to track + self.add_text_input("exp_track_count", "# of EXP Changes to Track", text="7") + + # Output widgets + self.add_text_input("time_to_level", "Time to Level", text="Calculating...") + self.add_text_input("time_between_kills", "Time Between Kills", text="N/A") + self.add_text_input("exp_per_kill", "EXP Per Kill", text="N/A") + + # Internal tracking lists + self.exp_history = [] + self.time_intervals = [] + self.last_exp_value = None + self.last_update_time = None + + # Timer to periodically process EXP changes + self.timer = QTimer() + self.timer.timeout.connect(self.process_exp_change) + self.timer.start(1000) # Check for updates every second + + def reset_tracking_arrays(self): + """ + Resets the EXP history and time interval arrays when a level-up is detected. + """ + self.exp_history.clear() + self.time_intervals.clear() + self.last_exp_value = None + self.last_update_time = None + + def process_exp_change(self): + """ + Monitors changes in EXP values and calculates various statistics. + """ + exp_value = data_manager.get_data().get("exp", None) + if exp_value is None: + return + + exp_track_count = self.get_property("exp_track_count") + try: + exp_track_count = int(exp_track_count) + except ValueError: + exp_track_count = 7 # Default to 7 if invalid input + + # Reset if EXP value decreases (indicating a level-up) + if self.last_exp_value is not None and exp_value < self.last_exp_value: + self.reset_tracking_arrays() + + if self.last_exp_value is not None and exp_value != self.last_exp_value: + current_time = time.time() + + # Store EXP change history + self.exp_history.append(exp_value) + if len(self.exp_history) > exp_track_count: + self.exp_history.pop(0) + + # Store time intervals + if self.last_update_time is not None: + interval = current_time - self.last_update_time + self.time_intervals.append(interval) + if len(self.time_intervals) > exp_track_count: + self.time_intervals.pop(0) + + # Perform calculations + self.calculate_time_to_level() + self.calculate_additional_metrics() + + # Update last tracking values + self.last_update_time = current_time + + self.last_exp_value = exp_value + + def calculate_time_to_level(self): + """ + Calculates the estimated time to reach level 100 based on EXP change history. + """ + if len(self.exp_history) < 2 or len(self.time_intervals) < 1: + self.set_property("time_to_level", "Insufficient data") + return + + exp_deltas = np.diff(self.exp_history) # Compute EXP change per interval + avg_exp_change = np.mean(exp_deltas) if len(exp_deltas) > 0 else 0 + avg_time_change = np.mean(self.time_intervals) + + if avg_exp_change <= 0: + self.set_property("time_to_level", "Not gaining EXP") + return + + current_exp = self.exp_history[-1] + remaining_exp = 100.0 - current_exp # Distance to level 100 + + estimated_time = (remaining_exp / avg_exp_change) * avg_time_change + + # Convert estimated time into hours, minutes, and seconds + hours = int(estimated_time // 3600) + minutes = int((estimated_time % 3600) // 60) + seconds = int(estimated_time % 60) + + time_str = f"{hours}h {minutes}m {seconds}s" + self.set_property("time_to_level", time_str) + + def calculate_additional_metrics(self): + """ + Calculates and updates the "Time Between Kills" and "EXP Per Kill". + """ + if len(self.time_intervals) > 0: + avg_time_between_kills = np.mean(self.time_intervals) + minutes = int(avg_time_between_kills // 60) + seconds = int(avg_time_between_kills % 60) + self.set_property("time_between_kills", f"{minutes}m {seconds}s") + else: + self.set_property("time_between_kills", "N/A") + + if len(self.exp_history) > 1: + exp_deltas = np.diff(self.exp_history) + avg_exp_per_kill = np.mean(exp_deltas) if len(exp_deltas) > 0 else 0 + self.set_property("exp_per_kill", f"{avg_exp_per_kill:.2f}%") + else: + self.set_property("exp_per_kill", "N/A") diff --git a/borealis.py b/borealis.py index dba6bee..f60d6c9 100644 --- a/borealis.py +++ b/borealis.py @@ -150,7 +150,7 @@ if __name__ == "__main__": # Create the NodeGraph controller # (the monkey-patch ensures NodeGraph won't crash if it tries QtGui.QUndoStack(self)) graph = NodeGraph() - graph.widget.setWindowTitle("Project Borealis - Flyff Information Overlay") + graph.widget.setWindowTitle("Project Borealis - Workflow Automation System") # Dynamically import custom node classes from the 'Nodes' package. custom_nodes = import_nodes_from_folder("Nodes")