Restructured project and implemented virtual python environments to isolate application. Added launch scripts too.
This commit is contained in:
BIN
Data/Nodes/Flyff/Resources/bars_template.png
Normal file
BIN
Data/Nodes/Flyff/Resources/bars_template.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.6 KiB |
50
Data/Nodes/Flyff/flyff_EXP_current.py
Normal file
50
Data/Nodes/Flyff/flyff_EXP_current.py
Normal file
@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Flyff EXP Node (Final Combined Version)
|
||||
- Pulls the EXP value directly from data_manager.py
|
||||
- Outputs only the "exp" value as a string
|
||||
- Uses color (48, 116, 143) for its output port
|
||||
- Displays "exp" in a text field labeled "Value"
|
||||
- Retrieves the port with self.outputs().get('value')
|
||||
"""
|
||||
|
||||
import time
|
||||
import traceback
|
||||
from OdenGraphQt import BaseNode
|
||||
from Modules import data_manager # Importing data_manager from Modules
|
||||
|
||||
class FlyffEXPCurrentNode(BaseNode):
|
||||
__identifier__ = 'bunny-lab.io.flyff_exp_current_node'
|
||||
NODE_NAME = 'Flyff - EXP'
|
||||
|
||||
def __init__(self):
|
||||
super(FlyffEXPCurrentNode, self).__init__()
|
||||
|
||||
# 1) Text input property named "value" for UI display
|
||||
self.add_text_input('value', 'Value', text='N/A')
|
||||
|
||||
# 2) Output port also named "value"
|
||||
self.add_output('value', color=(48, 116, 143))
|
||||
|
||||
self.set_name("Flyff - EXP")
|
||||
|
||||
def process_input(self):
|
||||
try:
|
||||
new_value = data_manager.get_data().get("exp", "N/A")
|
||||
new_value_str = str(new_value)
|
||||
self.set_property('value', new_value_str)
|
||||
self.transmit_data(new_value_str)
|
||||
except Exception as e:
|
||||
tb = traceback.format_exc()
|
||||
print(f"[ERROR] Exception in FlyffEXPCurrentNode: {e}\nTraceback:\n{tb}")
|
||||
|
||||
def transmit_data(self, data):
|
||||
output_port = self.outputs().get('value')
|
||||
if output_port and output_port.connected_ports():
|
||||
for connected_port in output_port.connected_ports():
|
||||
connected_node = connected_port.node()
|
||||
if hasattr(connected_node, 'receive_data'):
|
||||
try:
|
||||
connected_node.receive_data(data, source_port_name='value')
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Error transmitting data to {connected_node}: {e}")
|
93
Data/Nodes/Flyff/flyff_FP_current.py
Normal file
93
Data/Nodes/Flyff/flyff_FP_current.py
Normal file
@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Flyff FP Current Node (Final Combined Version)
|
||||
- Polls the API at http://127.0.0.1:5000/data
|
||||
- Outputs only the "fp_current" value as a string
|
||||
- Uses color (36, 116, 32) for its output port
|
||||
- Displays "fp_current" in a text field labeled "Value"
|
||||
- Retrieves the port with self.outputs().get('value')
|
||||
"""
|
||||
|
||||
import time
|
||||
import requests
|
||||
import traceback
|
||||
from OdenGraphQt import BaseNode
|
||||
|
||||
class FlyffFPCurrentNode(BaseNode):
|
||||
__identifier__ = 'bunny-lab.io.flyff_fp_current_node'
|
||||
NODE_NAME = 'Flyff - FP Current'
|
||||
|
||||
def __init__(self):
|
||||
super(FlyffFPCurrentNode, self).__init__()
|
||||
|
||||
# 1) Text input property named "value" for UI display
|
||||
self.add_text_input('value', 'Value', text='N/A')
|
||||
|
||||
# 2) Output port also named "value"
|
||||
self.add_output('value', color=(36, 116, 32))
|
||||
|
||||
self._api_down = True
|
||||
self._last_api_attempt = 0.0
|
||||
self._retry_interval = 5.0
|
||||
self._last_error_printed = 0.0
|
||||
|
||||
self.set_name("Flyff - FP Current (API Disconnected)")
|
||||
|
||||
def process_input(self):
|
||||
current_time = time.time()
|
||||
if self._api_down and (current_time - self._last_api_attempt < self._retry_interval):
|
||||
return
|
||||
|
||||
self._last_api_attempt = current_time
|
||||
|
||||
try:
|
||||
response = requests.get("http://127.0.0.1:5000/data", timeout=1)
|
||||
status_code = response.status_code
|
||||
print(f"[DEBUG] FlyffFPCurrentNode: HTTP Status Code = {status_code}")
|
||||
|
||||
if status_code == 200:
|
||||
try:
|
||||
data = response.json() or {}
|
||||
except ValueError:
|
||||
data = {}
|
||||
|
||||
if isinstance(data, list):
|
||||
data = {}
|
||||
|
||||
self._api_down = False
|
||||
self.set_name("Flyff - FP Current (API Connected)")
|
||||
|
||||
new_value = data.get("fp_current", "N/A")
|
||||
print(f"[DEBUG] FlyffFPCurrentNode: fp_current = {new_value}")
|
||||
|
||||
new_value_str = str(new_value)
|
||||
self.set_property('value', new_value_str)
|
||||
self.transmit_data(new_value_str)
|
||||
|
||||
else:
|
||||
self._handle_api_error(f"HTTP {status_code} from FlyffFPCurrentNode")
|
||||
self._api_down = True
|
||||
|
||||
except Exception as e:
|
||||
tb = traceback.format_exc()
|
||||
self._handle_api_error(f"Exception in FlyffFPCurrentNode: {e}\nTraceback:\n{tb}")
|
||||
self._api_down = True
|
||||
|
||||
def transmit_data(self, data):
|
||||
output_port = self.outputs().get('value')
|
||||
if output_port and output_port.connected_ports():
|
||||
for connected_port in output_port.connected_ports():
|
||||
connected_node = connected_port.node()
|
||||
if hasattr(connected_node, 'receive_data'):
|
||||
try:
|
||||
connected_node.receive_data(data, source_port_name='value')
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Error transmitting data to {connected_node}: {e}")
|
||||
|
||||
def _handle_api_error(self, msg):
|
||||
current_time = time.time()
|
||||
if (current_time - self._last_error_printed) >= self._retry_interval:
|
||||
print(f"[ERROR] {msg}")
|
||||
self._last_error_printed = current_time
|
||||
|
||||
self.set_name("Flyff - FP Current (API Disconnected)")
|
93
Data/Nodes/Flyff/flyff_FP_total.py
Normal file
93
Data/Nodes/Flyff/flyff_FP_total.py
Normal file
@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Flyff FP Total Node (Final Combined Version)
|
||||
- Polls the API at http://127.0.0.1:5000/data
|
||||
- Outputs only the "fp_total" value as a string
|
||||
- Uses color (36, 116, 32) for its output port
|
||||
- Displays "fp_total" in a text field labeled "Value"
|
||||
- Retrieves the port with self.outputs().get('value')
|
||||
"""
|
||||
|
||||
import time
|
||||
import requests
|
||||
import traceback
|
||||
from OdenGraphQt import BaseNode
|
||||
|
||||
class FlyffFPTotalNode(BaseNode):
|
||||
__identifier__ = 'bunny-lab.io.flyff_fp_total_node'
|
||||
NODE_NAME = 'Flyff - FP Total'
|
||||
|
||||
def __init__(self):
|
||||
super(FlyffFPTotalNode, self).__init__()
|
||||
|
||||
# 1) Text input property named "value" for UI display
|
||||
self.add_text_input('value', 'Value', text='N/A')
|
||||
|
||||
# 2) Output port also named "value"
|
||||
self.add_output('value', color=(36, 116, 32))
|
||||
|
||||
self._api_down = True
|
||||
self._last_api_attempt = 0.0
|
||||
self._retry_interval = 5.0
|
||||
self._last_error_printed = 0.0
|
||||
|
||||
self.set_name("Flyff - FP Total (API Disconnected)")
|
||||
|
||||
def process_input(self):
|
||||
current_time = time.time()
|
||||
if self._api_down and (current_time - self._last_api_attempt < self._retry_interval):
|
||||
return
|
||||
|
||||
self._last_api_attempt = current_time
|
||||
|
||||
try:
|
||||
response = requests.get("http://127.0.0.1:5000/data", timeout=1)
|
||||
status_code = response.status_code
|
||||
print(f"[DEBUG] FlyffFPTotalNode: HTTP Status Code = {status_code}")
|
||||
|
||||
if status_code == 200:
|
||||
try:
|
||||
data = response.json() or {}
|
||||
except ValueError:
|
||||
data = {}
|
||||
|
||||
if isinstance(data, list):
|
||||
data = {}
|
||||
|
||||
self._api_down = False
|
||||
self.set_name("Flyff - FP Total (API Connected)")
|
||||
|
||||
new_value = data.get("fp_total", "N/A")
|
||||
print(f"[DEBUG] FlyffFPTotalNode: fp_total = {new_value}")
|
||||
|
||||
new_value_str = str(new_value)
|
||||
self.set_property('value', new_value_str)
|
||||
self.transmit_data(new_value_str)
|
||||
|
||||
else:
|
||||
self._handle_api_error(f"HTTP {status_code} from FlyffFPTotalNode")
|
||||
self._api_down = True
|
||||
|
||||
except Exception as e:
|
||||
tb = traceback.format_exc()
|
||||
self._handle_api_error(f"Exception in FlyffFPTotalNode: {e}\nTraceback:\n{tb}")
|
||||
self._api_down = True
|
||||
|
||||
def transmit_data(self, data):
|
||||
output_port = self.outputs().get('value')
|
||||
if output_port and output_port.connected_ports():
|
||||
for connected_port in output_port.connected_ports():
|
||||
connected_node = connected_port.node()
|
||||
if hasattr(connected_node, 'receive_data'):
|
||||
try:
|
||||
connected_node.receive_data(data, source_port_name='value')
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Error transmitting data to {connected_node}: {e}")
|
||||
|
||||
def _handle_api_error(self, msg):
|
||||
current_time = time.time()
|
||||
if (current_time - self._last_error_printed) >= self._retry_interval:
|
||||
print(f"[ERROR] {msg}")
|
||||
self._last_error_printed = current_time
|
||||
|
||||
self.set_name("Flyff - FP Total (API Disconnected)")
|
112
Data/Nodes/Flyff/flyff_HP_current.py
Normal file
112
Data/Nodes/Flyff/flyff_HP_current.py
Normal file
@ -0,0 +1,112 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Flyff HP Current Node (Final Combined Version)
|
||||
- Polls the API at http://127.0.0.1:5000/data
|
||||
- Outputs only the "hp_current" value as a string
|
||||
- Uses color (126, 36, 57) for its output port
|
||||
- Displays "hp_current" in a text field labeled "Value"
|
||||
- Avoids "list indices must be integers" by retrieving the port with self.outputs().get('value')
|
||||
"""
|
||||
|
||||
import time
|
||||
import requests
|
||||
import traceback
|
||||
from OdenGraphQt import BaseNode
|
||||
|
||||
class FlyffHPCurrentNode(BaseNode):
|
||||
__identifier__ = 'bunny-lab.io.flyff_hp_current_node'
|
||||
NODE_NAME = 'Flyff - HP Current'
|
||||
|
||||
def __init__(self):
|
||||
super(FlyffHPCurrentNode, self).__init__()
|
||||
|
||||
# 1) Add a text input property named "value" for UI display
|
||||
self.add_text_input('value', 'Value', text='N/A')
|
||||
|
||||
# 2) Add an output port also named "value"
|
||||
self.add_output('value', color=(126, 36, 57))
|
||||
|
||||
# Start in "disconnected" state
|
||||
self._api_down = True
|
||||
self._last_api_attempt = 0.0
|
||||
self._retry_interval = 5.0
|
||||
self._last_error_printed = 0.0
|
||||
|
||||
# Default node title
|
||||
self.set_name("Flyff - HP Current (API Disconnected)")
|
||||
|
||||
def process_input(self):
|
||||
"""
|
||||
Called periodically by the global timer in borealis.py
|
||||
"""
|
||||
current_time = time.time()
|
||||
if self._api_down and (current_time - self._last_api_attempt < self._retry_interval):
|
||||
return
|
||||
|
||||
self._last_api_attempt = current_time
|
||||
|
||||
try:
|
||||
response = requests.get("http://127.0.0.1:5000/data", timeout=1)
|
||||
status_code = response.status_code
|
||||
print(f"[DEBUG] FlyffHPCurrentNode: HTTP Status Code = {status_code}")
|
||||
|
||||
if status_code == 200:
|
||||
# Attempt to parse JSON
|
||||
try:
|
||||
data = response.json() or {}
|
||||
except ValueError:
|
||||
data = {}
|
||||
|
||||
# If data is a list, ignore or convert to {}
|
||||
if isinstance(data, list):
|
||||
data = {}
|
||||
|
||||
# Mark node as connected
|
||||
self._api_down = False
|
||||
self.set_name("Flyff - HP Current (API Connected)")
|
||||
|
||||
# Retrieve hp_current (default "N/A" if missing)
|
||||
new_value = data.get("hp_current", "N/A")
|
||||
print(f"[DEBUG] FlyffHPCurrentNode: hp_current = {new_value}")
|
||||
|
||||
# Convert to string
|
||||
new_value_str = str(new_value)
|
||||
|
||||
# 3) Update the text input property so the user sees it
|
||||
self.set_property('value', new_value_str)
|
||||
|
||||
# 4) Transmit to downstream nodes
|
||||
self.transmit_data(new_value_str)
|
||||
|
||||
else:
|
||||
# Non-200 => disconnected
|
||||
self._handle_api_error(f"HTTP {status_code} from FlyffHPCurrentNode")
|
||||
self._api_down = True
|
||||
|
||||
except Exception as e:
|
||||
tb = traceback.format_exc()
|
||||
self._handle_api_error(f"Exception in FlyffHPCurrentNode: {e}\nTraceback:\n{tb}")
|
||||
self._api_down = True
|
||||
|
||||
def transmit_data(self, data):
|
||||
"""
|
||||
Sends 'data' to any connected node via the "value" port.
|
||||
(Uses self.outputs().get('value') instead of self.output('value'))
|
||||
"""
|
||||
output_port = self.outputs().get('value')
|
||||
if output_port and output_port.connected_ports():
|
||||
for connected_port in output_port.connected_ports():
|
||||
connected_node = connected_port.node()
|
||||
if hasattr(connected_node, 'receive_data'):
|
||||
try:
|
||||
connected_node.receive_data(data, source_port_name='value')
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Error transmitting data to {connected_node}: {e}")
|
||||
|
||||
def _handle_api_error(self, msg):
|
||||
current_time = time.time()
|
||||
if (current_time - self._last_error_printed) >= self._retry_interval:
|
||||
print(f"[ERROR] {msg}")
|
||||
self._last_error_printed = current_time
|
||||
|
||||
self.set_name("Flyff - HP Current (API Disconnected)")
|
93
Data/Nodes/Flyff/flyff_HP_total.py
Normal file
93
Data/Nodes/Flyff/flyff_HP_total.py
Normal file
@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Flyff HP Total Node (Final Combined Version)
|
||||
- Polls the API at http://127.0.0.1:5000/data
|
||||
- Outputs only the "hp_total" value as a string
|
||||
- Uses color (126, 36, 57) for its output port
|
||||
- Displays "hp_total" in a text field labeled "Value"
|
||||
- Retrieves the port with self.outputs().get('value')
|
||||
"""
|
||||
|
||||
import time
|
||||
import requests
|
||||
import traceback
|
||||
from OdenGraphQt import BaseNode
|
||||
|
||||
class FlyffHPTotalNode(BaseNode):
|
||||
__identifier__ = 'bunny-lab.io.flyff_hp_total_node'
|
||||
NODE_NAME = 'Flyff - HP Total'
|
||||
|
||||
def __init__(self):
|
||||
super(FlyffHPTotalNode, self).__init__()
|
||||
|
||||
# 1) Text input property named "value" for UI display
|
||||
self.add_text_input('value', 'Value', text='N/A')
|
||||
|
||||
# 2) Output port also named "value"
|
||||
self.add_output('value', color=(126, 36, 57))
|
||||
|
||||
self._api_down = True
|
||||
self._last_api_attempt = 0.0
|
||||
self._retry_interval = 5.0
|
||||
self._last_error_printed = 0.0
|
||||
|
||||
self.set_name("Flyff - HP Total (API Disconnected)")
|
||||
|
||||
def process_input(self):
|
||||
current_time = time.time()
|
||||
if self._api_down and (current_time - self._last_api_attempt < self._retry_interval):
|
||||
return
|
||||
|
||||
self._last_api_attempt = current_time
|
||||
|
||||
try:
|
||||
response = requests.get("http://127.0.0.1:5000/data", timeout=1)
|
||||
status_code = response.status_code
|
||||
print(f"[DEBUG] FlyffHPTotalNode: HTTP Status Code = {status_code}")
|
||||
|
||||
if status_code == 200:
|
||||
try:
|
||||
data = response.json() or {}
|
||||
except ValueError:
|
||||
data = {}
|
||||
|
||||
if isinstance(data, list):
|
||||
data = {}
|
||||
|
||||
self._api_down = False
|
||||
self.set_name("Flyff - HP Total (API Connected)")
|
||||
|
||||
new_value = data.get("hp_total", "N/A")
|
||||
print(f"[DEBUG] FlyffHPTotalNode: hp_total = {new_value}")
|
||||
|
||||
new_value_str = str(new_value)
|
||||
self.set_property('value', new_value_str)
|
||||
self.transmit_data(new_value_str)
|
||||
|
||||
else:
|
||||
self._handle_api_error(f"HTTP {status_code} from FlyffHPTotalNode")
|
||||
self._api_down = True
|
||||
|
||||
except Exception as e:
|
||||
tb = traceback.format_exc()
|
||||
self._handle_api_error(f"Exception in FlyffHPTotalNode: {e}\nTraceback:\n{tb}")
|
||||
self._api_down = True
|
||||
|
||||
def transmit_data(self, data):
|
||||
output_port = self.outputs().get('value')
|
||||
if output_port and output_port.connected_ports():
|
||||
for connected_port in output_port.connected_ports():
|
||||
connected_node = connected_port.node()
|
||||
if hasattr(connected_node, 'receive_data'):
|
||||
try:
|
||||
connected_node.receive_data(data, source_port_name='value')
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Error transmitting data to {connected_node}: {e}")
|
||||
|
||||
def _handle_api_error(self, msg):
|
||||
current_time = time.time()
|
||||
if (current_time - self._last_error_printed) >= self._retry_interval:
|
||||
print(f"[ERROR] {msg}")
|
||||
self._last_error_printed = current_time
|
||||
|
||||
self.set_name("Flyff - HP Total (API Disconnected)")
|
93
Data/Nodes/Flyff/flyff_MP_current.py
Normal file
93
Data/Nodes/Flyff/flyff_MP_current.py
Normal file
@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Flyff MP Current Node (Final Combined Version)
|
||||
- Polls the API at http://127.0.0.1:5000/data
|
||||
- Outputs only the "mp_current" value as a string
|
||||
- Uses color (35, 89, 144) for its output port
|
||||
- Displays "mp_current" in a text field labeled "Value"
|
||||
- Retrieves the port with self.outputs().get('value')
|
||||
"""
|
||||
|
||||
import time
|
||||
import requests
|
||||
import traceback
|
||||
from OdenGraphQt import BaseNode
|
||||
|
||||
class FlyffMPCurrentNode(BaseNode):
|
||||
__identifier__ = 'bunny-lab.io.flyff_mp_current_node'
|
||||
NODE_NAME = 'Flyff - MP Current'
|
||||
|
||||
def __init__(self):
|
||||
super(FlyffMPCurrentNode, self).__init__()
|
||||
|
||||
# 1) Text input property named "value" for UI display
|
||||
self.add_text_input('value', 'Value', text='N/A')
|
||||
|
||||
# 2) Output port also named "value"
|
||||
self.add_output('value', color=(35, 89, 144))
|
||||
|
||||
self._api_down = True
|
||||
self._last_api_attempt = 0.0
|
||||
self._retry_interval = 5.0
|
||||
self._last_error_printed = 0.0
|
||||
|
||||
self.set_name("Flyff - MP Current (API Disconnected)")
|
||||
|
||||
def process_input(self):
|
||||
current_time = time.time()
|
||||
if self._api_down and (current_time - self._last_api_attempt < self._retry_interval):
|
||||
return
|
||||
|
||||
self._last_api_attempt = current_time
|
||||
|
||||
try:
|
||||
response = requests.get("http://127.0.0.1:5000/data", timeout=1)
|
||||
status_code = response.status_code
|
||||
print(f"[DEBUG] FlyffMPCurrentNode: HTTP Status Code = {status_code}")
|
||||
|
||||
if status_code == 200:
|
||||
try:
|
||||
data = response.json() or {}
|
||||
except ValueError:
|
||||
data = {}
|
||||
|
||||
if isinstance(data, list):
|
||||
data = {}
|
||||
|
||||
self._api_down = False
|
||||
self.set_name("Flyff - MP Current (API Connected)")
|
||||
|
||||
new_value = data.get("mp_current", "N/A")
|
||||
print(f"[DEBUG] FlyffMPCurrentNode: mp_current = {new_value}")
|
||||
|
||||
new_value_str = str(new_value)
|
||||
self.set_property('value', new_value_str)
|
||||
self.transmit_data(new_value_str)
|
||||
|
||||
else:
|
||||
self._handle_api_error(f"HTTP {status_code} from FlyffMPCurrentNode")
|
||||
self._api_down = True
|
||||
|
||||
except Exception as e:
|
||||
tb = traceback.format_exc()
|
||||
self._handle_api_error(f"Exception in FlyffMPCurrentNode: {e}\nTraceback:\n{tb}")
|
||||
self._api_down = True
|
||||
|
||||
def transmit_data(self, data):
|
||||
output_port = self.outputs().get('value')
|
||||
if output_port and output_port.connected_ports():
|
||||
for connected_port in output_port.connected_ports():
|
||||
connected_node = connected_port.node()
|
||||
if hasattr(connected_node, 'receive_data'):
|
||||
try:
|
||||
connected_node.receive_data(data, source_port_name='value')
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Error transmitting data to {connected_node}: {e}")
|
||||
|
||||
def _handle_api_error(self, msg):
|
||||
current_time = time.time()
|
||||
if (current_time - self._last_error_printed) >= self._retry_interval:
|
||||
print(f"[ERROR] {msg}")
|
||||
self._last_error_printed = current_time
|
||||
|
||||
self.set_name("Flyff - MP Current (API Disconnected)")
|
93
Data/Nodes/Flyff/flyff_MP_total.py
Normal file
93
Data/Nodes/Flyff/flyff_MP_total.py
Normal file
@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Flyff MP Total Node (Final Combined Version)
|
||||
- Polls the API at http://127.0.0.1:5000/data
|
||||
- Outputs only the "mp_total" value as a string
|
||||
- Uses color (35, 89, 144) for its output port
|
||||
- Displays "mp_total" in a text field labeled "Value"
|
||||
- Retrieves the port with self.outputs().get('value')
|
||||
"""
|
||||
|
||||
import time
|
||||
import requests
|
||||
import traceback
|
||||
from OdenGraphQt import BaseNode
|
||||
|
||||
class FlyffMPTotalNode(BaseNode):
|
||||
__identifier__ = 'bunny-lab.io.flyff_mp_total_node'
|
||||
NODE_NAME = 'Flyff - MP Total'
|
||||
|
||||
def __init__(self):
|
||||
super(FlyffMPTotalNode, self).__init__()
|
||||
|
||||
# 1) Text input property named "value" for UI display
|
||||
self.add_text_input('value', 'Value', text='N/A')
|
||||
|
||||
# 2) Output port also named "value"
|
||||
self.add_output('value', color=(35, 89, 144))
|
||||
|
||||
self._api_down = True
|
||||
self._last_api_attempt = 0.0
|
||||
self._retry_interval = 5.0
|
||||
self._last_error_printed = 0.0
|
||||
|
||||
self.set_name("Flyff - MP Total (API Disconnected)")
|
||||
|
||||
def process_input(self):
|
||||
current_time = time.time()
|
||||
if self._api_down and (current_time - self._last_api_attempt < self._retry_interval):
|
||||
return
|
||||
|
||||
self._last_api_attempt = current_time
|
||||
|
||||
try:
|
||||
response = requests.get("http://127.0.0.1:5000/data", timeout=1)
|
||||
status_code = response.status_code
|
||||
print(f"[DEBUG] FlyffMPTotalNode: HTTP Status Code = {status_code}")
|
||||
|
||||
if status_code == 200:
|
||||
try:
|
||||
data = response.json() or {}
|
||||
except ValueError:
|
||||
data = {}
|
||||
|
||||
if isinstance(data, list):
|
||||
data = {}
|
||||
|
||||
self._api_down = False
|
||||
self.set_name("Flyff - MP Total (API Connected)")
|
||||
|
||||
new_value = data.get("mp_total", "N/A")
|
||||
print(f"[DEBUG] FlyffMPTotalNode: mp_total = {new_value}")
|
||||
|
||||
new_value_str = str(new_value)
|
||||
self.set_property('value', new_value_str)
|
||||
self.transmit_data(new_value_str)
|
||||
|
||||
else:
|
||||
self._handle_api_error(f"HTTP {status_code} from FlyffMPTotalNode")
|
||||
self._api_down = True
|
||||
|
||||
except Exception as e:
|
||||
tb = traceback.format_exc()
|
||||
self._handle_api_error(f"Exception in FlyffMPTotalNode: {e}\nTraceback:\n{tb}")
|
||||
self._api_down = True
|
||||
|
||||
def transmit_data(self, data):
|
||||
output_port = self.outputs().get('value')
|
||||
if output_port and output_port.connected_ports():
|
||||
for connected_port in output_port.connected_ports():
|
||||
connected_node = connected_port.node()
|
||||
if hasattr(connected_node, 'receive_data'):
|
||||
try:
|
||||
connected_node.receive_data(data, source_port_name='value')
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Error transmitting data to {connected_node}: {e}")
|
||||
|
||||
def _handle_api_error(self, msg):
|
||||
current_time = time.time()
|
||||
if (current_time - self._last_error_printed) >= self._retry_interval:
|
||||
print(f"[ERROR] {msg}")
|
||||
self._last_error_printed = current_time
|
||||
|
||||
self.set_name("Flyff - MP Total (API Disconnected)")
|
105
Data/Nodes/Flyff/flyff_character_status_node.py
Normal file
105
Data/Nodes/Flyff/flyff_character_status_node.py
Normal file
@ -0,0 +1,105 @@
|
||||
#!/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.
|
||||
"""
|
||||
|
||||
import re
|
||||
from OdenGraphQt import BaseNode
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
from PyQt5.QtCore import QTimer # Corrected import
|
||||
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.
|
||||
"""
|
||||
raw_text = data_collector.get_raw_text(self.region_id)
|
||||
# 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)
|
||||
|
||||
# Update the 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}%")
|
141
Data/Nodes/Flyff/flyff_leveling_predictor_node.py
Normal file
141
Data/Nodes/Flyff/flyff_leveling_predictor_node.py
Normal file
@ -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")
|
134
Data/Nodes/Flyff/flyff_low_health_alert_node.py
Normal file
134
Data/Nodes/Flyff/flyff_low_health_alert_node.py
Normal file
@ -0,0 +1,134 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Standardized Flyff Low Health Alert Node:
|
||||
- Monitors an input value (1 = health alert, 0 = normal).
|
||||
- Displays a visual alert and plays a sound if enabled.
|
||||
- Uses a global update timer for processing.
|
||||
- Automatically processes float, int, and string values.
|
||||
"""
|
||||
|
||||
import time
|
||||
from OdenGraphQt import BaseNode
|
||||
from Qt import QtCore, QtWidgets, QtGui
|
||||
|
||||
try:
|
||||
import winsound
|
||||
HAS_WINSOUND = True
|
||||
except ImportError:
|
||||
winsound = None
|
||||
HAS_WINSOUND = False
|
||||
|
||||
class OverlayCanvas(QtWidgets.QWidget):
|
||||
"""
|
||||
UI overlay for displaying a red warning box, which can be repositioned by dragging.
|
||||
"""
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
screen_geo = QtWidgets.QApplication.primaryScreen().geometry()
|
||||
self.setGeometry(screen_geo)
|
||||
self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint)
|
||||
self.setAttribute(QtCore.Qt.WA_TranslucentBackground, True)
|
||||
self.setVisible(False)
|
||||
self.helper_LowHealthAlert = QtCore.QRect(250, 300, 900, 35)
|
||||
self.dragging = False
|
||||
self.drag_offset = None
|
||||
|
||||
def paintEvent(self, event):
|
||||
if not self.isVisible():
|
||||
return
|
||||
painter = QtGui.QPainter(self)
|
||||
painter.setPen(QtCore.Qt.NoPen)
|
||||
painter.setBrush(QtGui.QColor(255, 0, 0))
|
||||
painter.drawRect(self.helper_LowHealthAlert)
|
||||
font = QtGui.QFont("Arial", 14, QtGui.QFont.Bold)
|
||||
painter.setFont(font)
|
||||
painter.setPen(QtGui.QColor(255, 255, 255))
|
||||
text_x = self.helper_LowHealthAlert.center().x() - 50
|
||||
text_y = self.helper_LowHealthAlert.center().y() + 5
|
||||
painter.drawText(text_x, text_y, "LOW HEALTH")
|
||||
|
||||
def toggle_alert(self, state):
|
||||
self.setVisible(state == 1)
|
||||
self.update()
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
if event.button() == QtCore.Qt.LeftButton:
|
||||
if self.helper_LowHealthAlert.contains(event.pos()):
|
||||
self.dragging = True
|
||||
self.drag_offset = event.pos() - self.helper_LowHealthAlert.topLeft()
|
||||
super().mousePressEvent(event)
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
if self.dragging:
|
||||
new_top_left = event.pos() - self.drag_offset
|
||||
self.helper_LowHealthAlert.moveTo(new_top_left)
|
||||
self.update()
|
||||
super().mouseMoveEvent(event)
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
if event.button() == QtCore.Qt.LeftButton:
|
||||
self.dragging = False
|
||||
super().mouseReleaseEvent(event)
|
||||
|
||||
class FlyffLowHealthAlertNode(BaseNode):
|
||||
__identifier__ = 'bunny-lab.io.flyff_low_health_alert_node'
|
||||
NODE_NAME = 'Flyff - Low Health Alert'
|
||||
|
||||
overlay_instance = None
|
||||
last_beep_time = 0
|
||||
BEEP_INTERVAL_SECONDS = 2
|
||||
|
||||
def __init__(self):
|
||||
super(FlyffLowHealthAlertNode, self).__init__()
|
||||
self.add_checkbox('cb_1', '', 'Sound Alert', True)
|
||||
self.add_checkbox('cb_2', '', 'Visual Alert', True)
|
||||
self.add_input('Toggle (1 = On | 0 = Off)', color=(200, 100, 0))
|
||||
self.add_text_input('value', 'Current Value', text='0')
|
||||
self.add_combo_menu('beep_interval', 'Beep Interval', items=["0.5s", "1.0s", "2.0s"])
|
||||
|
||||
if not FlyffLowHealthAlertNode.overlay_instance:
|
||||
FlyffLowHealthAlertNode.overlay_instance = OverlayCanvas()
|
||||
FlyffLowHealthAlertNode.overlay_instance.show()
|
||||
|
||||
def process_input(self):
|
||||
input_port = self.input(0)
|
||||
value = input_port.connected_ports()[0].node().get_property('value') if input_port.connected_ports() else "0"
|
||||
self.receive_data(value)
|
||||
|
||||
def receive_data(self, data, source_port_name=None):
|
||||
try:
|
||||
if isinstance(data, str):
|
||||
data = float(data) if '.' in data else int(data)
|
||||
if isinstance(data, (float, int)):
|
||||
data = 1 if data > 1 else 0 if data <= 0 else int(data)
|
||||
else:
|
||||
data = 0
|
||||
except ValueError:
|
||||
data = 0
|
||||
|
||||
self.set_property('value', str(data))
|
||||
if self.get_property('cb_2'):
|
||||
FlyffLowHealthAlertNode.overlay_instance.toggle_alert(data)
|
||||
self.handle_beep(data)
|
||||
|
||||
def handle_beep(self, input_value):
|
||||
# Update beep interval from the dropdown property
|
||||
interval_str = self.get_property('beep_interval')
|
||||
if interval_str.endswith("s"):
|
||||
interval_seconds = float(interval_str[:-1])
|
||||
else:
|
||||
interval_seconds = float(interval_str)
|
||||
self.BEEP_INTERVAL_SECONDS = interval_seconds
|
||||
|
||||
if input_value == 1 and self.get_property('cb_1'):
|
||||
current_time = time.time()
|
||||
if (current_time - FlyffLowHealthAlertNode.last_beep_time) >= self.BEEP_INTERVAL_SECONDS:
|
||||
FlyffLowHealthAlertNode.last_beep_time = current_time
|
||||
self.play_beep()
|
||||
|
||||
def play_beep(self):
|
||||
if HAS_WINSOUND:
|
||||
winsound.Beep(376, 100)
|
||||
else:
|
||||
print('\a', end='')
|
Reference in New Issue
Block a user