fix(api): correct attachment field parsing for all attachment types
- Fixed NexusAttachment.from_api() to parse correct API structure: - Amplifiers: Damage values from Properties.Damage - Scopes: Skill bonuses from Properties.SkillModification/SkillBonus, zoom from Properties.Zoom - Absorbers: Absorption from Economy.Absorption - Added zoom and absorption fields to NexusAttachment - Updated attachment selector UI to show type-specific columns - Added zoom and absorption to preview panel
This commit is contained in:
parent
4ef03d96c8
commit
e8f0d7860e
|
|
@ -153,63 +153,59 @@ class NexusAttachment(NexusItem):
|
||||||
range_bonus: Decimal
|
range_bonus: Decimal
|
||||||
decay: Decimal
|
decay: Decimal
|
||||||
efficiency_bonus: Decimal
|
efficiency_bonus: Decimal
|
||||||
|
zoom: int = 0 # For scopes
|
||||||
|
absorption: Decimal = Decimal("0") # For absorbers
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_api(cls, data: Dict[str, Any]) -> "NexusAttachment":
|
def from_api(cls, data: Dict[str, Any]) -> "NexusAttachment":
|
||||||
"""Create from API response."""
|
"""Create from API response."""
|
||||||
props = data.get('Properties', {})
|
props = data.get('Properties', {})
|
||||||
|
|
||||||
# Debug: Log available properties
|
# Get Economy data
|
||||||
logger.debug(f"Attachment {data.get('Name')} properties: {props.keys()}")
|
economy = props.get('Economy', {}) or {}
|
||||||
|
decay = safe_decimal(economy.get('Decay'))
|
||||||
# Try multiple possible field names for each stat
|
efficiency = safe_decimal(economy.get('Efficiency'))
|
||||||
# Damage bonus might be in Damage, DamageBonus, or DamageIncrease
|
|
||||||
damage_bonus = Decimal("0")
|
|
||||||
for key in ['Damage', 'DamageBonus', 'DamageIncrease', 'dmg', 'damage']:
|
|
||||||
if key in props and props[key] is not None:
|
|
||||||
damage_bonus = safe_decimal(props[key])
|
|
||||||
break
|
|
||||||
|
|
||||||
# Range bonus
|
|
||||||
range_bonus = Decimal("0")
|
|
||||||
for key in ['Range', 'RangeBonus', 'RangeIncrease', 'range']:
|
|
||||||
if key in props and props[key] is not None:
|
|
||||||
range_bonus = safe_decimal(props[key])
|
|
||||||
break
|
|
||||||
|
|
||||||
# Decay - check in Economy dict or top-level
|
|
||||||
decay = Decimal("0")
|
|
||||||
if 'Economy' in props and isinstance(props['Economy'], dict):
|
|
||||||
decay = safe_decimal(props['Economy'].get('Decay'))
|
|
||||||
for key in ['Decay', 'decay', 'Cost']:
|
|
||||||
if key in props and props[key] is not None:
|
|
||||||
decay = safe_decimal(props[key])
|
|
||||||
break
|
|
||||||
|
|
||||||
# Efficiency
|
|
||||||
efficiency_bonus = Decimal("0")
|
|
||||||
if 'Economy' in props and isinstance(props['Economy'], dict):
|
|
||||||
efficiency_bonus = safe_decimal(props['Economy'].get('Efficiency'))
|
|
||||||
for key in ['Efficiency', 'efficiency', 'Eco', 'Eff']:
|
|
||||||
if key in props and props[key] is not None:
|
|
||||||
efficiency_bonus = safe_decimal(props[key])
|
|
||||||
break
|
|
||||||
|
|
||||||
# Determine attachment type from API type or name
|
# Determine attachment type from API type or name
|
||||||
api_type = props.get('Type', '').lower()
|
api_type = props.get('Type', '').lower()
|
||||||
name = data.get('Name', '').lower()
|
name = data.get('Name', '').lower()
|
||||||
|
|
||||||
if 'amplifier' in api_type or 'amp' in name:
|
if 'amplifier' in name or 'amp' in name:
|
||||||
attachment_type = 'amplifier'
|
attachment_type = 'amplifier'
|
||||||
elif 'scope' in api_type or 'scope' in name:
|
elif 'scope' in api_type or 'scope' in name:
|
||||||
attachment_type = 'scope'
|
attachment_type = 'scope'
|
||||||
elif 'sight' in api_type or 'sight' in name:
|
elif 'sight' in api_type or 'sight' in name:
|
||||||
attachment_type = 'sight'
|
attachment_type = 'sight'
|
||||||
elif 'absorber' in api_type or 'absorber' in name:
|
elif 'absorber' in name or 'extender' in name:
|
||||||
attachment_type = 'absorber'
|
attachment_type = 'absorber'
|
||||||
else:
|
else:
|
||||||
attachment_type = api_type or 'unknown'
|
attachment_type = api_type or 'unknown'
|
||||||
|
|
||||||
|
# Parse based on attachment type
|
||||||
|
damage_bonus = Decimal("0")
|
||||||
|
range_bonus = Decimal("0")
|
||||||
|
zoom = 0
|
||||||
|
absorption = Decimal("0")
|
||||||
|
|
||||||
|
if attachment_type == 'amplifier':
|
||||||
|
# Amplifiers have damage in Properties.Damage
|
||||||
|
damage_data = props.get('Damage', {}) or {}
|
||||||
|
# Sum up all non-null damage values
|
||||||
|
for dmg_type, value in damage_data.items():
|
||||||
|
if value is not None:
|
||||||
|
damage_bonus += safe_decimal(value)
|
||||||
|
|
||||||
|
elif attachment_type == 'scope':
|
||||||
|
# Scopes have skill modification and zoom
|
||||||
|
skill_mod = safe_decimal(props.get('SkillModification'))
|
||||||
|
skill_bonus = safe_decimal(props.get('SkillBonus'))
|
||||||
|
range_bonus = skill_mod + skill_bonus
|
||||||
|
zoom = int(props.get('Zoom', 0) or 0)
|
||||||
|
|
||||||
|
elif attachment_type == 'absorber':
|
||||||
|
# Absorbers have absorption in Economy
|
||||||
|
absorption = safe_decimal(economy.get('Absorption'))
|
||||||
|
|
||||||
return cls(
|
return cls(
|
||||||
id=data.get('Id', 0),
|
id=data.get('Id', 0),
|
||||||
name=data.get('Name', 'Unknown'),
|
name=data.get('Name', 'Unknown'),
|
||||||
|
|
@ -219,7 +215,9 @@ class NexusAttachment(NexusItem):
|
||||||
damage_bonus=damage_bonus,
|
damage_bonus=damage_bonus,
|
||||||
range_bonus=range_bonus,
|
range_bonus=range_bonus,
|
||||||
decay=decay,
|
decay=decay,
|
||||||
efficiency_bonus=efficiency_bonus,
|
efficiency_bonus=efficiency,
|
||||||
|
zoom=zoom,
|
||||||
|
absorption=absorption,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
"""
|
||||||
|
Debug script to check Entropia Nexus API attachment data
|
||||||
|
"""
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
|
||||||
|
def fetch_and_debug(endpoint: str, name: str):
|
||||||
|
url = f"https://api.entropianexus.com/{endpoint}"
|
||||||
|
print(f"\n{'='*60}")
|
||||||
|
print(f"Fetching {name} from {endpoint}")
|
||||||
|
print(f"{'='*60}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
resp = requests.get(url, timeout=30)
|
||||||
|
resp.raise_for_status()
|
||||||
|
data = resp.json()
|
||||||
|
|
||||||
|
if isinstance(data, list):
|
||||||
|
print(f"Got {len(data)} items")
|
||||||
|
|
||||||
|
if len(data) > 0:
|
||||||
|
# Show first item structure
|
||||||
|
first = data[0]
|
||||||
|
print(f"\nFirst item ({first.get('Name', 'Unknown')}):")
|
||||||
|
print(json.dumps(first, indent=2))
|
||||||
|
|
||||||
|
# Check a few more items for patterns
|
||||||
|
print(f"\nChecking first 5 items for Properties structure:")
|
||||||
|
for i, item in enumerate(data[:5]):
|
||||||
|
props = item.get('Properties', {})
|
||||||
|
print(f"\n{i+1}. {item.get('Name')}:")
|
||||||
|
print(f" Properties keys: {list(props.keys())}")
|
||||||
|
if 'Economy' in props:
|
||||||
|
print(f" Economy: {props['Economy']}")
|
||||||
|
else:
|
||||||
|
print(f"Unexpected response type: {type(data)}")
|
||||||
|
print(json.dumps(data, indent=2)[:1000])
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error: {e}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# Check all attachment endpoints
|
||||||
|
fetch_and_debug("weaponamplifiers", "Weapon Amplifiers")
|
||||||
|
fetch_and_debug("weaponvisionattachments", "Weapon Vision Attachments (Scopes/Sights)")
|
||||||
|
fetch_and_debug("absorbers", "Absorbers")
|
||||||
|
|
@ -136,12 +136,16 @@ class AttachmentSelectorDialog(QDialog):
|
||||||
self.preview_type = QLabel("-")
|
self.preview_type = QLabel("-")
|
||||||
self.preview_damage = QLabel("-")
|
self.preview_damage = QLabel("-")
|
||||||
self.preview_range = QLabel("-")
|
self.preview_range = QLabel("-")
|
||||||
|
self.preview_zoom = QLabel("-")
|
||||||
|
self.preview_absorption = QLabel("-")
|
||||||
self.preview_decay = QLabel("-")
|
self.preview_decay = QLabel("-")
|
||||||
self.preview_efficiency = QLabel("-")
|
self.preview_efficiency = QLabel("-")
|
||||||
preview_layout.addRow("Name:", self.preview_name)
|
preview_layout.addRow("Name:", self.preview_name)
|
||||||
preview_layout.addRow("Type:", self.preview_type)
|
preview_layout.addRow("Type:", self.preview_type)
|
||||||
preview_layout.addRow("Damage Bonus:", self.preview_damage)
|
preview_layout.addRow("Damage Bonus:", self.preview_damage)
|
||||||
preview_layout.addRow("Range Bonus:", self.preview_range)
|
preview_layout.addRow("Range/Acc Bonus:", self.preview_range)
|
||||||
|
preview_layout.addRow("Zoom:", self.preview_zoom)
|
||||||
|
preview_layout.addRow("Absorption:", self.preview_absorption)
|
||||||
preview_layout.addRow("Decay:", self.preview_decay)
|
preview_layout.addRow("Decay:", self.preview_decay)
|
||||||
preview_layout.addRow("Efficiency:", self.preview_efficiency)
|
preview_layout.addRow("Efficiency:", self.preview_efficiency)
|
||||||
layout.addWidget(self.preview_group)
|
layout.addWidget(self.preview_group)
|
||||||
|
|
@ -172,9 +176,9 @@ class AttachmentSelectorDialog(QDialog):
|
||||||
search_layout.addWidget(clear_btn)
|
search_layout.addWidget(clear_btn)
|
||||||
layout.addLayout(search_layout)
|
layout.addLayout(search_layout)
|
||||||
|
|
||||||
# Tree
|
# Tree - different columns for different attachment types
|
||||||
tree = QTreeWidget()
|
tree = QTreeWidget()
|
||||||
tree.setHeaderLabels(["Name", "Type", "Dmg+", "Rng+", "Decay", "Efficiency"])
|
tree.setHeaderLabels(["Name", "Type", "Dmg+", "Rng/Acc", "Zoom", "Decay", "Eff%"])
|
||||||
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)
|
||||||
|
|
@ -233,29 +237,45 @@ class AttachmentSelectorDialog(QDialog):
|
||||||
item.setText(0, att.name)
|
item.setText(0, att.name)
|
||||||
item.setText(1, att.attachment_type.title())
|
item.setText(1, att.attachment_type.title())
|
||||||
|
|
||||||
# Format damage with + sign if positive
|
# Column 2: Damage bonus (for amplifiers)
|
||||||
dmg_text = f"+{att.damage_bonus}" if att.damage_bonus > 0 else str(att.damage_bonus)
|
dmg_text = f"+{att.damage_bonus}" if att.damage_bonus > 0 else "-"
|
||||||
item.setText(2, dmg_text)
|
item.setText(2, dmg_text)
|
||||||
|
|
||||||
# Format range with + sign if positive
|
# Column 3: Range/Accuracy bonus (for scopes)
|
||||||
rng_text = f"+{att.range_bonus}" if att.range_bonus > 0 else str(att.range_bonus)
|
if att.attachment_type == 'scope' and att.range_bonus > 0:
|
||||||
|
rng_text = f"+{att.range_bonus:.1f}"
|
||||||
|
elif att.attachment_type == 'absorber' and att.absorption > 0:
|
||||||
|
rng_text = f"{att.absorption * 100:.0f}%"
|
||||||
|
else:
|
||||||
|
rng_text = "-"
|
||||||
item.setText(3, rng_text)
|
item.setText(3, rng_text)
|
||||||
|
|
||||||
# Format decay (typically in PEC)
|
# Column 4: Zoom (for scopes)
|
||||||
item.setText(4, f"{att.decay:.4f}")
|
if att.attachment_type == 'scope' and att.zoom > 0:
|
||||||
|
zoom_text = f"{att.zoom}x"
|
||||||
|
else:
|
||||||
|
zoom_text = "-"
|
||||||
|
item.setText(4, zoom_text)
|
||||||
|
|
||||||
# Format efficiency as percentage
|
# Column 5: Decay (in PEC)
|
||||||
eff_text = f"{att.efficiency_bonus:.2f}%" if att.efficiency_bonus > 0 else "-"
|
item.setText(5, f"{att.decay:.3f}")
|
||||||
item.setText(5, eff_text)
|
|
||||||
|
# Column 6: Efficiency %
|
||||||
|
if att.efficiency_bonus > 0:
|
||||||
|
eff_text = f"{att.efficiency_bonus:.0f}%"
|
||||||
|
else:
|
||||||
|
eff_text = "-"
|
||||||
|
item.setText(6, eff_text)
|
||||||
|
|
||||||
# Color code by type
|
# Color code by type
|
||||||
if att.attachment_type == "amplifier":
|
if att.attachment_type == "amplifier":
|
||||||
item.setForeground(0, QColor("#ff9800")) # Orange
|
item.setForeground(0, QColor("#ff9800")) # Orange
|
||||||
# Highlight positive damage values
|
|
||||||
if att.damage_bonus > 0:
|
if att.damage_bonus > 0:
|
||||||
item.setForeground(2, QColor("#7FFF7F"))
|
item.setForeground(2, QColor("#7FFF7F"))
|
||||||
elif att.attachment_type == "scope":
|
elif att.attachment_type == "scope":
|
||||||
item.setForeground(0, QColor("#2196f3")) # Blue
|
item.setForeground(0, QColor("#2196f3")) # Blue
|
||||||
|
if att.zoom > 0:
|
||||||
|
item.setForeground(4, QColor("#00FFFF"))
|
||||||
elif att.attachment_type == "sight":
|
elif att.attachment_type == "sight":
|
||||||
item.setForeground(0, QColor("#4caf50")) # Green
|
item.setForeground(0, QColor("#4caf50")) # Green
|
||||||
elif att.attachment_type == "absorber":
|
elif att.attachment_type == "absorber":
|
||||||
|
|
@ -311,10 +331,47 @@ class AttachmentSelectorDialog(QDialog):
|
||||||
"""Update preview panel."""
|
"""Update preview panel."""
|
||||||
self.preview_name.setText(attachment.name)
|
self.preview_name.setText(attachment.name)
|
||||||
self.preview_type.setText(attachment.attachment_type.title())
|
self.preview_type.setText(attachment.attachment_type.title())
|
||||||
|
|
||||||
|
# Damage bonus (amplifiers)
|
||||||
|
if attachment.damage_bonus > 0:
|
||||||
self.preview_damage.setText(f"+{attachment.damage_bonus}")
|
self.preview_damage.setText(f"+{attachment.damage_bonus}")
|
||||||
self.preview_range.setText(f"+{attachment.range_bonus}")
|
self.preview_damage.setStyleSheet("color: #7FFF7F;")
|
||||||
self.preview_decay.setText(f"{attachment.decay:.2f} PEC")
|
else:
|
||||||
self.preview_efficiency.setText(f"{attachment.efficiency_bonus:.1f}%")
|
self.preview_damage.setText("-")
|
||||||
|
self.preview_damage.setStyleSheet("")
|
||||||
|
|
||||||
|
# Range/Accuracy bonus (scopes)
|
||||||
|
if attachment.range_bonus > 0:
|
||||||
|
self.preview_range.setText(f"+{attachment.range_bonus:.1f}")
|
||||||
|
self.preview_range.setStyleSheet("color: #7FFF7F;")
|
||||||
|
else:
|
||||||
|
self.preview_range.setText("-")
|
||||||
|
self.preview_range.setStyleSheet("")
|
||||||
|
|
||||||
|
# Zoom (scopes)
|
||||||
|
if attachment.zoom > 0:
|
||||||
|
self.preview_zoom.setText(f"{attachment.zoom}x")
|
||||||
|
self.preview_zoom.setStyleSheet("color: #00FFFF;")
|
||||||
|
else:
|
||||||
|
self.preview_zoom.setText("-")
|
||||||
|
self.preview_zoom.setStyleSheet("")
|
||||||
|
|
||||||
|
# Absorption (absorbers)
|
||||||
|
if attachment.absorption > 0:
|
||||||
|
self.preview_absorption.setText(f"{attachment.absorption * 100:.1f}%")
|
||||||
|
self.preview_absorption.setStyleSheet("color: #9c27b0;")
|
||||||
|
else:
|
||||||
|
self.preview_absorption.setText("-")
|
||||||
|
self.preview_absorption.setStyleSheet("")
|
||||||
|
|
||||||
|
# Decay
|
||||||
|
self.preview_decay.setText(f"{attachment.decay:.4f} PEC")
|
||||||
|
|
||||||
|
# Efficiency
|
||||||
|
if attachment.efficiency_bonus > 0:
|
||||||
|
self.preview_efficiency.setText(f"{attachment.efficiency_bonus:.0f}%")
|
||||||
|
else:
|
||||||
|
self.preview_efficiency.setText("-")
|
||||||
|
|
||||||
def _clear_preview(self):
|
def _clear_preview(self):
|
||||||
"""Clear preview panel."""
|
"""Clear preview panel."""
|
||||||
|
|
@ -322,8 +379,13 @@ class AttachmentSelectorDialog(QDialog):
|
||||||
self.preview_type.setText("-")
|
self.preview_type.setText("-")
|
||||||
self.preview_damage.setText("-")
|
self.preview_damage.setText("-")
|
||||||
self.preview_range.setText("-")
|
self.preview_range.setText("-")
|
||||||
|
self.preview_zoom.setText("-")
|
||||||
|
self.preview_absorption.setText("-")
|
||||||
self.preview_decay.setText("-")
|
self.preview_decay.setText("-")
|
||||||
self.preview_efficiency.setText("-")
|
self.preview_efficiency.setText("-")
|
||||||
|
# Reset stylesheets
|
||||||
|
for label in [self.preview_damage, self.preview_range, self.preview_zoom, self.preview_absorption]:
|
||||||
|
label.setStyleSheet("")
|
||||||
|
|
||||||
def _on_double_click(self, item, column):
|
def _on_double_click(self, item, column):
|
||||||
"""Handle double click."""
|
"""Handle double click."""
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue