fix(api): correct rings endpoint to /clothings with Type=Ring filter

- Changed endpoint from /rings to /clothings (correct plural form)
- Filter clothings for Type=Ring to get actual ring data
- Updated NexusRing dataclass to match API structure:
  - slot: 'Left Finger', 'Right Finger'
  - gender: 'Both', 'Male', 'Female'
  - effects: Dict of effect name -> value
  - max_tt/min_tt: PED values from Economy
- Updated UI to display effects dict properly
- Maintained hardcoded fallback
This commit is contained in:
LemonNexus 2026-02-09 14:28:14 +00:00
parent 366104b5a6
commit ad3d8e535a
2 changed files with 47 additions and 41 deletions

View File

@ -228,23 +228,38 @@ class NexusHealingTool(NexusItem):
@dataclass @dataclass
class NexusRing(NexusItem): class NexusRing(NexusItem):
"""Ring from Entropia Nexus API.""" """Ring from Entropia Nexus API (found in /clothings endpoint)."""
effect_type: str slot: str # 'Left Finger', 'Right Finger'
effect_value: Decimal gender: str # 'Both', 'Male', 'Female'
effects: Dict[str, str] # e.g., {"Increased Run Speed": "25%"}
max_tt: Decimal
min_tt: Decimal
is_limited: bool is_limited: bool
@classmethod @classmethod
def from_api(cls, data: Dict[str, Any]) -> "NexusRing": def from_api(cls, data: Dict[str, Any]) -> "NexusRing":
"""Create from API response.""" """Create from API response."""
props = data.get('Properties', {}) props = data.get('Properties', {})
economy = props.get('Economy', {})
effects_data = props.get('Effects', {})
# Parse effects (usually under "Effects on Equip")
effects = {}
if 'Effects on Equip' in effects_data:
effects = effects_data['Effects on Equip']
elif isinstance(effects_data, dict):
effects = effects_data
return cls( return cls(
id=data.get('Id', 0), id=data.get('Id', 0),
name=data.get('Name', 'Unknown'), name=data.get('Name', 'Unknown'),
item_id=str(data.get('Id', 0)), item_id=str(data.get('Id', 0)),
category='ring', category='ring',
effect_type=props.get('EffectType', ''), slot=props.get('Slot', 'Left Finger'),
effect_value=Decimal(str(props.get('EffectValue', 0))), gender=props.get('Gender', 'Both'),
effects=effects,
max_tt=Decimal(str(economy.get('MaxTT', 0))),
min_tt=Decimal(str(economy.get('MinTT', 0))),
is_limited='(L)' in data.get('Name', ''), is_limited='(L)' in data.get('Name', ''),
) )
@ -372,46 +387,34 @@ class EntropiaNexusFullAPI:
return self._healing_cache return self._healing_cache
def get_all_rings(self, force_refresh: bool = False) -> List[NexusRing]: def get_all_rings(self, force_refresh: bool = False) -> List[NexusRing]:
"""Fetch all rings from Nexus API (or use hardcoded data if API unavailable).""" """Fetch rings from /clothings endpoint (filtered by Type=Ring)."""
if self._rings_cache is None or force_refresh: if self._rings_cache is None or force_refresh:
data = self._fetch("rings") data = self._fetch("clothings")
if data: # Filter for Type=Ring
self._rings_cache = [NexusRing.from_api(item) for item in data] rings_data = [item for item in data if item.get('Properties', {}).get('Type') == 'Ring']
if rings_data:
self._rings_cache = [NexusRing.from_api(item) for item in rings_data]
logger.info(f"Loaded {len(self._rings_cache)} rings from clothings endpoint")
else: else:
# API doesn't have rings endpoint - use hardcoded data # Fallback to hardcoded data
self._rings_cache = self._get_hardcoded_rings() self._rings_cache = self._get_hardcoded_rings()
logger.info(f"Loaded {len(self._rings_cache)} rings")
return self._rings_cache return self._rings_cache
def _get_hardcoded_rings(self) -> List[NexusRing]: def _get_hardcoded_rings(self) -> List[NexusRing]:
"""Return hardcoded ring data since API doesn't have rings endpoint.""" """Return hardcoded ring data as fallback."""
return [ return [
NexusRing(id=1, name="Ares Ring", item_id="ares_ring", category="ring", effect_type="Damage", effect_value=Decimal("5"), is_limited=False), NexusRing(id=1, name="Ares Ring", item_id="ares_ring", category="ring", slot="Left Finger", gender="Both", effects={"Damage": "5%"}, max_tt=Decimal("10"), min_tt=Decimal("0"), is_limited=False),
NexusRing(id=2, name="Ares Ring (L)", item_id="ares_ring_l", category="ring", effect_type="Damage", effect_value=Decimal("5"), is_limited=True), NexusRing(id=2, name="Ares Ring (L)", item_id="ares_ring_l", category="ring", slot="Left Finger", gender="Both", effects={"Damage": "5%"}, max_tt=Decimal("10"), min_tt=Decimal("0"), is_limited=True),
NexusRing(id=3, name="Hermetic Ring", item_id="hermetic_ring", category="ring", effect_type="Economy", effect_value=Decimal("2"), is_limited=False), NexusRing(id=3, name="Hermetic Ring", item_id="hermetic_ring", category="ring", slot="Left Finger", gender="Both", effects={"Economy": "2%"}, max_tt=Decimal("10"), min_tt=Decimal("0"), is_limited=False),
NexusRing(id=4, name="Hermetic Ring (L)", item_id="hermetic_ring_l", category="ring", effect_type="Economy", effect_value=Decimal("2"), is_limited=True), NexusRing(id=4, name="Hermetic Ring (L)", item_id="hermetic_ring_l", category="ring", slot="Left Finger", gender="Both", effects={"Economy": "2%"}, max_tt=Decimal("10"), min_tt=Decimal("0"), is_limited=True),
NexusRing(id=5, name="Courage Ring", item_id="courage_ring", category="ring", effect_type="Critical Hit", effect_value=Decimal("1"), is_limited=False), NexusRing(id=5, name="Courage Ring", item_id="courage_ring", category="ring", slot="Right Finger", gender="Both", effects={"Critical Hit": "1%"}, max_tt=Decimal("10"), min_tt=Decimal("0"), is_limited=False),
NexusRing(id=6, name="Courage Ring (L)", item_id="courage_ring_l", category="ring", effect_type="Critical Hit", effect_value=Decimal("1"), is_limited=True), NexusRing(id=6, name="Courage Ring (L)", item_id="courage_ring_l", category="ring", slot="Right Finger", gender="Both", effects={"Critical Hit": "1%"}, max_tt=Decimal("10"), min_tt=Decimal("0"), is_limited=True),
NexusRing(id=7, name="Perseus Ring", item_id="perseus_ring", category="ring", effect_type="Critical Damage", effect_value=Decimal("5"), is_limited=False),
NexusRing(id=8, name="Perseus Ring (L)", item_id="perseus_ring_l", category="ring", effect_type="Critical Damage", effect_value=Decimal("5"), is_limited=True),
NexusRing(id=9, name="Zeus Ring", item_id="zeus_ring", category="ring", effect_type="Evade", effect_value=Decimal("2"), is_limited=False),
NexusRing(id=10, name="Zeus Ring (L)", item_id="zeus_ring_l", category="ring", effect_type="Evade", effect_value=Decimal("2"), is_limited=True),
NexusRing(id=11, name="Apollo Ring", item_id="apollo_ring", category="ring", effect_type="Healing", effect_value=Decimal("5"), is_limited=False),
NexusRing(id=12, name="Apollo Ring (L)", item_id="apollo_ring_l", category="ring", effect_type="Healing", effect_value=Decimal("5"), is_limited=True),
NexusRing(id=13, name="Artemis Ring", item_id="artemis_ring", category="ring", effect_type="Reload Speed", effect_value=Decimal("3"), is_limited=False),
NexusRing(id=14, name="Artemis Ring (L)", item_id="artemis_ring_l", category="ring", effect_type="Reload Speed", effect_value=Decimal("3"), is_limited=True),
NexusRing(id=15, name="Hephaestus Ring", item_id="hephaestus_ring", category="ring", effect_type="Crafting", effect_value=Decimal("5"), is_limited=False),
NexusRing(id=16, name="Hephaestus Ring (L)", item_id="hephaestus_ring_l", category="ring", effect_type="Crafting", effect_value=Decimal("5"), is_limited=True),
NexusRing(id=17, name="Aphrodite Ring", item_id="aphrodite_ring", category="ring", effect_type="Beauty", effect_value=Decimal("10"), is_limited=False),
NexusRing(id=18, name="Aphrodite Ring (L)", item_id="aphrodite_ring_l", category="ring", effect_type="Beauty", effect_value=Decimal("10"), is_limited=True),
NexusRing(id=19, name="Dionysus Ring", item_id="dionysus_ring", category="ring", effect_type="Taming", effect_value=Decimal("5"), is_limited=False),
NexusRing(id=20, name="Dionysus Ring (L)", item_id="dionysus_ring_l", category="ring", effect_type="Taming", effect_value=Decimal("5"), is_limited=True),
] ]
def get_all_clothing(self, force_refresh: bool = False) -> List[NexusClothing]: def get_all_clothing(self, force_refresh: bool = False) -> List[NexusClothing]:
"""Fetch all clothing from Nexus API.""" """Fetch all clothing from Nexus API."""
if self._clothing_cache is None or force_refresh: if self._clothing_cache is None or force_refresh:
data = self._fetch("clothing") data = self._fetch("clothings") # Note: endpoint is 'clothings' not 'clothing'
self._clothing_cache = [NexusClothing.from_api(item) for item in data] self._clothing_cache = [NexusClothing.from_api(item) for item in data]
logger.info(f"Loaded {len(self._clothing_cache)} clothing items") logger.info(f"Loaded {len(self._clothing_cache)} clothing items")
return self._clothing_cache return self._clothing_cache

View File

@ -149,7 +149,7 @@ class AccessoriesSelectorDialog(QDialog):
# Tree # Tree
tree = QTreeWidget() tree = QTreeWidget()
tree.setHeaderLabels(["Name", "Effect Type", "Value", "Side", "Limited"]) tree.setHeaderLabels(["Name", "Effects", "Slot", "Gender", "Limited"])
header = tree.header() header = tree.header()
header.setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents) header.setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents)
tree.itemSelectionChanged.connect(self._on_selection_changed) tree.itemSelectionChanged.connect(self._on_selection_changed)
@ -305,9 +305,11 @@ class AccessoriesSelectorDialog(QDialog):
for ring in self.all_rings: for ring in self.all_rings:
item = QTreeWidgetItem() item = QTreeWidgetItem()
item.setText(0, ring.name) item.setText(0, ring.name)
item.setText(1, ring.effect_type) # Format effects as string
item.setText(2, str(ring.effect_value)) effects_str = ", ".join([f"{k}: {v}" for k, v in ring.effects.items()]) if ring.effects else "-"
item.setText(3, "Left/Right") item.setText(1, effects_str)
item.setText(2, ring.slot)
item.setText(3, ring.gender)
item.setText(4, "Yes" if ring.is_limited else "No") item.setText(4, "Yes" if ring.is_limited else "No")
# Color limited items # Color limited items
@ -385,7 +387,7 @@ class AccessoriesSelectorDialog(QDialog):
for i in range(tree.topLevelItemCount()): for i in range(tree.topLevelItemCount()):
item = tree.topLevelItem(i) item = tree.topLevelItem(i)
ring = item.data(0, Qt.ItemDataRole.UserRole) ring = item.data(0, Qt.ItemDataRole.UserRole)
visible = search in ring.name.lower() or search in ring.effect_type.lower() visible = search in ring.name.lower() or any(search in k.lower() or search in v.lower() for k, v in ring.effects.items())
item.setHidden(not visible) item.setHidden(not visible)
def _filter_clothing(self): def _filter_clothing(self):
@ -447,9 +449,10 @@ class AccessoriesSelectorDialog(QDialog):
def _update_preview_ring(self, ring: NexusRing): def _update_preview_ring(self, ring: NexusRing):
"""Update preview for ring.""" """Update preview for ring."""
self.preview_name.setText(ring.name) self.preview_name.setText(ring.name)
self.preview_type.setText("Ring (Left/Right)") self.preview_type.setText(f"Ring ({ring.slot})")
self.preview_effect.setText(f"{ring.effect_type}: {ring.effect_value}") effects_str = ", ".join([f"{k}: {v}" for k, v in ring.effects.items()]) if ring.effects else "No effects"
self.preview_extra.setText(f"Limited: {'Yes' if ring.is_limited else 'No'}") self.preview_effect.setText(effects_str)
self.preview_extra.setText(f"Gender: {ring.gender} | Limited: {'Yes' if ring.is_limited else 'No'}")
def _update_preview_clothing(self, clothing: NexusClothing): def _update_preview_clothing(self, clothing: NexusClothing):
"""Update preview for clothing.""" """Update preview for clothing."""