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
|
||||
decay: Decimal
|
||||
efficiency_bonus: Decimal
|
||||
zoom: int = 0 # For scopes
|
||||
absorption: Decimal = Decimal("0") # For absorbers
|
||||
|
||||
@classmethod
|
||||
def from_api(cls, data: Dict[str, Any]) -> "NexusAttachment":
|
||||
"""Create from API response."""
|
||||
props = data.get('Properties', {})
|
||||
|
||||
# Debug: Log available properties
|
||||
logger.debug(f"Attachment {data.get('Name')} properties: {props.keys()}")
|
||||
|
||||
# Try multiple possible field names for each stat
|
||||
# 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
|
||||
# Get Economy data
|
||||
economy = props.get('Economy', {}) or {}
|
||||
decay = safe_decimal(economy.get('Decay'))
|
||||
efficiency = safe_decimal(economy.get('Efficiency'))
|
||||
|
||||
# Determine attachment type from API type or name
|
||||
api_type = props.get('Type', '').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'
|
||||
elif 'scope' in api_type or 'scope' in name:
|
||||
attachment_type = 'scope'
|
||||
elif 'sight' in api_type or 'sight' in name:
|
||||
attachment_type = 'sight'
|
||||
elif 'absorber' in api_type or 'absorber' in name:
|
||||
elif 'absorber' in name or 'extender' in name:
|
||||
attachment_type = 'absorber'
|
||||
else:
|
||||
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(
|
||||
id=data.get('Id', 0),
|
||||
name=data.get('Name', 'Unknown'),
|
||||
|
|
@ -219,7 +215,9 @@ class NexusAttachment(NexusItem):
|
|||
damage_bonus=damage_bonus,
|
||||
range_bonus=range_bonus,
|
||||
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_damage = QLabel("-")
|
||||
self.preview_range = QLabel("-")
|
||||
self.preview_zoom = QLabel("-")
|
||||
self.preview_absorption = QLabel("-")
|
||||
self.preview_decay = QLabel("-")
|
||||
self.preview_efficiency = QLabel("-")
|
||||
preview_layout.addRow("Name:", self.preview_name)
|
||||
preview_layout.addRow("Type:", self.preview_type)
|
||||
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("Efficiency:", self.preview_efficiency)
|
||||
layout.addWidget(self.preview_group)
|
||||
|
|
@ -172,9 +176,9 @@ class AttachmentSelectorDialog(QDialog):
|
|||
search_layout.addWidget(clear_btn)
|
||||
layout.addLayout(search_layout)
|
||||
|
||||
# Tree
|
||||
# Tree - different columns for different attachment types
|
||||
tree = QTreeWidget()
|
||||
tree.setHeaderLabels(["Name", "Type", "Dmg+", "Rng+", "Decay", "Efficiency"])
|
||||
tree.setHeaderLabels(["Name", "Type", "Dmg+", "Rng/Acc", "Zoom", "Decay", "Eff%"])
|
||||
header = tree.header()
|
||||
header.setSectionResizeMode(QHeaderView.ResizeMode.ResizeToContents)
|
||||
tree.itemSelectionChanged.connect(self._on_selection_changed)
|
||||
|
|
@ -233,29 +237,45 @@ class AttachmentSelectorDialog(QDialog):
|
|||
item.setText(0, att.name)
|
||||
item.setText(1, att.attachment_type.title())
|
||||
|
||||
# Format damage with + sign if positive
|
||||
dmg_text = f"+{att.damage_bonus}" if att.damage_bonus > 0 else str(att.damage_bonus)
|
||||
# Column 2: Damage bonus (for amplifiers)
|
||||
dmg_text = f"+{att.damage_bonus}" if att.damage_bonus > 0 else "-"
|
||||
item.setText(2, dmg_text)
|
||||
|
||||
# Format range with + sign if positive
|
||||
rng_text = f"+{att.range_bonus}" if att.range_bonus > 0 else str(att.range_bonus)
|
||||
# Column 3: Range/Accuracy bonus (for scopes)
|
||||
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)
|
||||
|
||||
# Format decay (typically in PEC)
|
||||
item.setText(4, f"{att.decay:.4f}")
|
||||
# Column 4: Zoom (for scopes)
|
||||
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
|
||||
eff_text = f"{att.efficiency_bonus:.2f}%" if att.efficiency_bonus > 0 else "-"
|
||||
item.setText(5, eff_text)
|
||||
# Column 5: Decay (in PEC)
|
||||
item.setText(5, f"{att.decay:.3f}")
|
||||
|
||||
# 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
|
||||
if att.attachment_type == "amplifier":
|
||||
item.setForeground(0, QColor("#ff9800")) # Orange
|
||||
# Highlight positive damage values
|
||||
if att.damage_bonus > 0:
|
||||
item.setForeground(2, QColor("#7FFF7F"))
|
||||
elif att.attachment_type == "scope":
|
||||
item.setForeground(0, QColor("#2196f3")) # Blue
|
||||
if att.zoom > 0:
|
||||
item.setForeground(4, QColor("#00FFFF"))
|
||||
elif att.attachment_type == "sight":
|
||||
item.setForeground(0, QColor("#4caf50")) # Green
|
||||
elif att.attachment_type == "absorber":
|
||||
|
|
@ -311,10 +331,47 @@ class AttachmentSelectorDialog(QDialog):
|
|||
"""Update preview panel."""
|
||||
self.preview_name.setText(attachment.name)
|
||||
self.preview_type.setText(attachment.attachment_type.title())
|
||||
self.preview_damage.setText(f"+{attachment.damage_bonus}")
|
||||
self.preview_range.setText(f"+{attachment.range_bonus}")
|
||||
self.preview_decay.setText(f"{attachment.decay:.2f} PEC")
|
||||
self.preview_efficiency.setText(f"{attachment.efficiency_bonus:.1f}%")
|
||||
|
||||
# Damage bonus (amplifiers)
|
||||
if attachment.damage_bonus > 0:
|
||||
self.preview_damage.setText(f"+{attachment.damage_bonus}")
|
||||
self.preview_damage.setStyleSheet("color: #7FFF7F;")
|
||||
else:
|
||||
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):
|
||||
"""Clear preview panel."""
|
||||
|
|
@ -322,8 +379,13 @@ class AttachmentSelectorDialog(QDialog):
|
|||
self.preview_type.setText("-")
|
||||
self.preview_damage.setText("-")
|
||||
self.preview_range.setText("-")
|
||||
self.preview_zoom.setText("-")
|
||||
self.preview_absorption.setText("-")
|
||||
self.preview_decay.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):
|
||||
"""Handle double click."""
|
||||
|
|
|
|||
Loading…
Reference in New Issue