Milestone Fixed Character Node

This commit is contained in:
2025-02-12 00:53:42 -07:00
parent 78e763c05c
commit 75f68b1f59
5 changed files with 176 additions and 52 deletions

View File

@ -2,8 +2,8 @@
"""
Character Status Node
This node represents the character's status. It has no input ports and four output ports:
- HP, MP, FP, EXP.
This node represents the character's status. It has seven output ports:
- HP: Current, HP: Total, MP: Current, MP: Total, FP: Current, FP: Total, EXP.
It polls an API endpoint (http://127.0.0.1:5000/data) every 500 ms to update its values.
If the API call is successful, the node's title is set to "Character Status (API Connected)".
If the API is down or returns an error, the title is set to "Character Status (API Disconnected)".
@ -12,6 +12,7 @@ If the API is down or returns an error, the title is set to "Character Status (A
from NodeGraphQt import BaseNode
from Qt import QtCore, QtGui
import requests
import traceback
def get_draw_stat_port(color, border_color=None, alpha=127):
"""
@ -24,19 +25,16 @@ def get_draw_stat_port(color, border_color=None, alpha=127):
def painter_func(painter, rect, info):
painter.save()
# Draw the port circle.
pen = QtGui.QPen(QtGui.QColor(*border_color))
pen.setWidth(1.8)
painter.setPen(pen)
semi_transparent_color = QtGui.QColor(color[0], color[1], color[2], alpha)
painter.setBrush(semi_transparent_color)
painter.drawEllipse(rect)
# Draw the label and current value.
port = info.get('port')
if port is not None:
node = port.node()
stat = port.name()
# Use the node's 'values' dictionary if available.
value = node.values.get(stat, "N/A") if hasattr(node, 'values') else "N/A"
text_rect = rect.adjusted(rect.width() + 4, 0, rect.width() + 70, 0)
painter.setPen(QtGui.QColor(0, 0, 0))
@ -51,16 +49,27 @@ class CharacterStatusNode(BaseNode):
def __init__(self):
super(CharacterStatusNode, self).__init__()
# Initialize the output values as a dictionary.
self.values = {"HP": "N/A", "MP": "N/A", "FP": "N/A", "EXP": "N/A"}
# Add output ports for each stat with custom painters.
self.add_output("HP", painter_func=get_draw_stat_port((255, 0, 0))) # Red for HP
self.add_output("MP", painter_func=get_draw_stat_port((0, 0, 255))) # Blue for MP
self.add_output("FP", painter_func=get_draw_stat_port((0, 255, 0))) # Green for FP
self.add_output("EXP", painter_func=get_draw_stat_port((127, 255, 212))) # Aquamarine for EXP
# Set an initial title.
# Define exact expected keys to avoid transformation mismatches
self.values = {
"HP: Current": "N/A", "HP: Total": "N/A",
"MP: Current": "N/A", "MP: Total": "N/A",
"FP: Current": "N/A", "FP: Total": "N/A",
"EXP": "N/A"
}
# Add output ports
self.add_output("HP: Current", painter_func=get_draw_stat_port((255, 0, 0)))
self.add_output("HP: Total", painter_func=get_draw_stat_port((255, 0, 0)))
self.add_output("MP: Current", painter_func=get_draw_stat_port((0, 0, 255)))
self.add_output("MP: Total", painter_func=get_draw_stat_port((0, 0, 255)))
self.add_output("FP: Current", painter_func=get_draw_stat_port((0, 255, 0)))
self.add_output("FP: Total", painter_func=get_draw_stat_port((0, 255, 0)))
self.add_output("EXP", painter_func=get_draw_stat_port((127, 255, 212)))
self.set_name("Character Status (API Disconnected)")
# Create a QTimer that polls the API every 500ms.
# Start polling timer
self.timer = QtCore.QTimer()
self.timer.timeout.connect(self.poll_api)
self.timer.start(500)
@ -68,22 +77,77 @@ class CharacterStatusNode(BaseNode):
def poll_api(self):
"""
Polls the API endpoint to retrieve the latest character stats and updates
the node's internal values. Expects a JSON response with keys:
- "hp", "mp", "fp", "exp"
the node's internal values.
"""
try:
response = requests.get("http://127.0.0.1:5000/data", timeout=1)
if response.status_code == 200:
data = response.json()
# Update the values dictionary.
self.values["HP"] = data.get("hp", "0/0")
self.values["MP"] = data.get("mp", "0/0")
self.values["FP"] = data.get("fp", "0/0")
self.values["EXP"] = data.get("exp", "0.0000")
self.set_name("Character Status (API Connected)")
self.update()
if isinstance(data, dict):
try:
for key, value in data.items():
# Ensure the keys match the expected ones exactly
formatted_key = {
"hp_current": "HP: Current",
"hp_total": "HP: Total",
"mp_current": "MP: Current",
"mp_total": "MP: Total",
"fp_current": "FP: Current",
"fp_total": "FP: Total",
"exp": "EXP"
}.get(key, key) # Use mapping or fallback to raw key
if formatted_key in self.values:
self.values[formatted_key] = str(value)
else:
print(f"[WARNING] Unexpected API key: {key} (not mapped)")
self.set_name("Character Status (API Connected)")
self.update()
self.transmit_data()
except Exception as e:
print("[ERROR] Error processing API response data:", e)
print("[ERROR] Stack Trace:\n", traceback.format_exc())
else:
print("[ERROR] Unexpected API response format (not a dict):", data)
self.set_name("Character Status (API Disconnected)")
else:
print(f"[ERROR] API request failed with status code {response.status_code}")
self.set_name("Character Status (API Disconnected)")
except Exception as e:
self.set_name("Character Status (API Disconnected)")
print("Error polling API in CharacterStatusNode:", e)
print("[ERROR] Error polling API in CharacterStatusNode:", str(e))
print("[ERROR] Stack Trace:\n", traceback.format_exc())
def transmit_data(self):
"""
Sends the updated character stats to connected nodes.
"""
for stat, value in self.values.items():
try:
port = self.get_output(stat)
if port is None:
print(f"[ERROR] Port '{stat}' not found in node outputs. Skipping...")
continue
if port.connected_ports():
for connected_port in port.connected_ports():
connected_node = connected_port.node()
if hasattr(connected_node, 'receive_data'):
try:
connected_node.receive_data(value, stat)
except Exception as e:
print(f"[ERROR] Error transmitting data to {connected_node}: {e}")
print("[ERROR] Stack Trace:\n", traceback.format_exc())
else:
print(f"[WARNING] Connected node {connected_node} does not have receive_data method.")
except Exception as e:
print(f"[ERROR] Error while handling port {stat}: {e}")
print("[ERROR] Stack Trace:\n", traceback.format_exc())