fix(armor): match Entropia Nexus slot naming exactly

- TORSO (was CHEST) - Harness/Chest piece
- LEGS (was THIGHS) - Thigh Guards
- Updated Frontier set with correct slots
- Display names match Nexus: Head, Torso, Arms, Hands, Legs, Shins, Feet

Now matches Nexus JSON export structure exactly
This commit is contained in:
LemonNexus 2026-02-09 11:06:46 +00:00
parent fd2b34e395
commit 9e55c751b9
1 changed files with 139 additions and 139 deletions

View File

@ -19,21 +19,21 @@ from enum import Enum, auto
class ArmorSlot(Enum):
"""Armor slot types in Entropia Universe.
Standard 7-piece armor structure:
Standard 7-piece armor structure (matches Entropia Nexus):
- Head (Helmet)
- Chest (Harness)
- Arms (Arm Guards - both arms)
- Hands (Gloves - both hands)
- Thighs (Thigh Guards)
- Torso (Harness/Chest)
- Arms (Arm Guards)
- Hands (Gloves)
- Legs (Thigh Guards)
- Shins (Shin Guards)
- Feet (Foot Guards)
"""
HEAD = "head"
CHEST = "chest" # Harness
ARMS = "arms" # Arm Guards (both arms)
HANDS = "hands" # Gloves (both hands)
THIGHS = "thighs" # Thigh Guards
TORSO = "torso" # Harness/Chest
ARMS = "arms" # Arm Guards
HANDS = "hands" # Gloves
LEGS = "legs" # Thigh Guards
SHINS = "shins" # Shin Guards
FEET = "feet" # Foot Guards
@ -41,10 +41,10 @@ class ArmorSlot(Enum):
# Full set of 7 slots
ALL_ARMOR_SLOTS = [
ArmorSlot.HEAD,
ArmorSlot.CHEST,
ArmorSlot.TORSO,
ArmorSlot.ARMS,
ArmorSlot.HANDS,
ArmorSlot.THIGHS,
ArmorSlot.LEGS,
ArmorSlot.SHINS,
ArmorSlot.FEET,
]
@ -62,18 +62,18 @@ class ProtectionProfile:
cold: Decimal = Decimal("0")
acid: Decimal = Decimal("0")
electric: Decimal = Decimal("0")
def get_total(self) -> Decimal:
"""Get total protection across all types."""
return (
self.stab + self.cut + self.impact + self.penetration +
self.shrapnel + self.burn + self.cold + self.acid + self.electric
)
def get_effective_against(self, damage_type: str) -> Decimal:
"""Get protection value for a specific damage type."""
return getattr(self, damage_type.lower(), Decimal("0"))
def add(self, other: "ProtectionProfile") -> "ProtectionProfile":
"""Add another protection profile to this one."""
return ProtectionProfile(
@ -87,7 +87,7 @@ class ProtectionProfile:
acid=self.acid + other.acid,
electric=self.electric + other.electric,
)
def subtract(self, other: "ProtectionProfile") -> "ProtectionProfile":
"""Subtract another protection profile from this one."""
return ProtectionProfile(
@ -101,7 +101,7 @@ class ProtectionProfile:
acid=max(Decimal("0"), self.acid - other.acid),
electric=max(Decimal("0"), self.electric - other.electric),
)
def to_dict(self) -> Dict[str, str]:
"""Convert to dictionary with string values."""
return {
@ -115,7 +115,7 @@ class ProtectionProfile:
'acid': str(self.acid),
'electric': str(self.electric),
}
@classmethod
def from_dict(cls, data: Dict[str, str]) -> "ProtectionProfile":
"""Create from dictionary."""
@ -137,7 +137,7 @@ class ArmorPlate:
"""
Armor plating that attaches to armor pieces.
Plates act as a shield layer - they take damage FIRST.
Official Decay Formula (VU 15.15):
Decay (PEC) = damage_absorbed * 0.05 * (1 - durability/100000)
"""
@ -146,35 +146,35 @@ class ArmorPlate:
protection: ProtectionProfile = field(default_factory=ProtectionProfile)
durability: int = 2000 # Plate durability affects economy
block_chance: Decimal = Decimal("0") # Chance to nullify hit completely (no decay)
# Constants
BASE_DECAY_FACTOR: Decimal = Decimal("0.05")
MAX_DURABILITY: int = 100000
def get_total_protection(self) -> Decimal:
"""Get total protection value."""
return self.protection.get_total()
def get_decay_for_damage(self, damage_absorbed: Decimal) -> Decimal:
"""
Calculate decay for absorbing damage using official formula.
Formula: Decay (PEC) = damage_absorbed * 0.05 * (1 - durability/100000)
Args:
damage_absorbed: Amount of damage the plate actually absorbed
Returns:
Decay in PEC
"""
if damage_absorbed <= 0:
return Decimal("0")
durability_factor = Decimal("1") - Decimal(self.durability) / Decimal(self.MAX_DURABILITY)
decay_pec = damage_absorbed * self.BASE_DECAY_FACTOR * durability_factor
return decay_pec
def get_hp_per_pec(self) -> Decimal:
"""
Calculate economy rating (hp per pec).
@ -184,11 +184,11 @@ class ArmorPlate:
if durability_factor <= 0:
return Decimal("0")
return Decimal("1") / (self.BASE_DECAY_FACTOR * durability_factor)
def get_effective_protection(self, damage_type: str) -> Decimal:
"""Get protection value for a specific damage type."""
return self.protection.get_effective_against(damage_type)
def to_dict(self) -> Dict:
"""Convert to dictionary."""
return {
@ -198,7 +198,7 @@ class ArmorPlate:
'durability': self.durability,
'block_chance': str(self.block_chance),
}
@classmethod
def from_dict(cls, data: Dict) -> "ArmorPlate":
"""Create from dictionary."""
@ -216,10 +216,10 @@ class ArmorPiece:
"""
Individual armor piece (e.g., Ghost Helmet, Shogun Harness).
Each piece protects one slot and can have one plate attached.
Official Decay Formula (VU 15.15):
Decay (PEC) = damage_absorbed * 0.05 * (1 - durability/100000)
Economy varies by durability:
- Ghost (2000 dur): 20.41 hp/pec
- Gremlin (2950 dur): 20.61 hp/pec
@ -232,51 +232,51 @@ class ArmorPiece:
protection: ProtectionProfile = field(default_factory=ProtectionProfile)
durability: int = 2000 # Durability affects economy
weight: Decimal = Decimal("1.0") # Weight in kg
# Base decay factor: 0.05 PEC per HP (20 hp/pec standard)
BASE_DECAY_FACTOR: Decimal = Decimal("0.05")
MAX_DURABILITY: int = 100000
# Optional plate attachment
attached_plate: Optional[ArmorPlate] = None
def get_base_protection(self) -> ProtectionProfile:
"""Get base protection without plate."""
return self.protection
def get_total_protection(self) -> ProtectionProfile:
"""Get total protection including plate."""
if self.attached_plate:
return self.protection.add(self.attached_plate.protection)
return self.protection
def get_total_protection_value(self) -> Decimal:
"""Get total protection value including plate."""
return self.get_total_protection().get_total()
def get_decay_for_damage(self, damage_absorbed: Decimal) -> Decimal:
"""
Calculate armor decay for absorbing damage.
Official Formula (VU 15.15):
Decay (PEC) = damage_absorbed * 0.05 * (1 - durability/100000)
Args:
damage_absorbed: Amount of damage the armor actually absorbed
Returns:
Decay in PEC
"""
if damage_absorbed <= 0:
return Decimal("0")
# Calculate economy factor based on durability
# Higher durability = better economy (less decay)
durability_factor = Decimal("1") - Decimal(self.durability) / Decimal(self.MAX_DURABILITY)
decay_pec = damage_absorbed * self.BASE_DECAY_FACTOR * durability_factor
return decay_pec
def get_hp_per_pec(self) -> Decimal:
"""
Calculate economy rating (hp per pec).
@ -286,31 +286,31 @@ class ArmorPiece:
if durability_factor <= 0:
return Decimal("0")
return Decimal("1") / (self.BASE_DECAY_FACTOR * durability_factor)
def attach_plate(self, plate: ArmorPlate) -> bool:
"""Attach a plate to this armor piece."""
self.attached_plate = plate
return True
def remove_plate(self) -> Optional[ArmorPlate]:
"""Remove and return the attached plate."""
plate = self.attached_plate
self.attached_plate = None
return plate
def get_slot_display_name(self) -> str:
"""Get human-readable slot name."""
"""Get human-readable slot name (matches Entropia Nexus)."""
slot_names = {
ArmorSlot.HEAD: "Helmet",
ArmorSlot.CHEST: "Harness",
ArmorSlot.ARMS: "Arm Guards",
ArmorSlot.HANDS: "Gloves",
ArmorSlot.THIGHS: "Thigh Guards",
ArmorSlot.SHINS: "Shin Guards",
ArmorSlot.FEET: "Foot Guards",
ArmorSlot.HEAD: "Head",
ArmorSlot.TORSO: "Torso",
ArmorSlot.ARMS: "Arms",
ArmorSlot.HANDS: "Hands",
ArmorSlot.LEGS: "Legs",
ArmorSlot.SHINS: "Shins",
ArmorSlot.FEET: "Feet",
}
return slot_names.get(self.slot, self.slot.value)
def to_dict(self) -> Dict:
"""Convert to dictionary."""
return {
@ -323,7 +323,7 @@ class ArmorPiece:
'weight': str(self.weight),
'attached_plate': self.attached_plate.to_dict() if self.attached_plate else None,
}
@classmethod
def from_dict(cls, data: Dict) -> "ArmorPiece":
"""Create from dictionary."""
@ -351,33 +351,33 @@ class ArmorSet:
set_id: str
pieces: Dict[ArmorSlot, ArmorPiece] = field(default_factory=dict)
set_bonus: Optional[ProtectionProfile] = None # Some sets have bonuses
def __post_init__(self):
"""Ensure all pieces reference this set."""
set_name = self.name.replace(" Set", "")
for slot, piece in self.pieces.items():
piece.set_name = set_name
def get_piece(self, slot: ArmorSlot) -> Optional[ArmorPiece]:
"""Get armor piece for a specific slot."""
return self.pieces.get(slot)
def is_complete(self) -> bool:
"""Check if set has all 7 pieces."""
return len(self.pieces) == 7 and all(slot in self.pieces for slot in ALL_ARMOR_SLOTS)
def get_total_protection(self) -> ProtectionProfile:
"""Get total protection from all pieces including plates."""
total = ProtectionProfile()
for piece in self.pieces.values():
total = total.add(piece.get_total_protection())
# Add set bonus if complete
if self.set_bonus and self.is_complete():
total = total.add(self.set_bonus)
return total
def get_total_decay_per_hit(self) -> Decimal:
"""
Get total decay per hit across all pieces (including plates).
@ -388,23 +388,23 @@ class ArmorSet:
# Using a typical hit value of 10 hp for estimation
typical_hit = Decimal("10")
total = Decimal("0")
for piece in self.pieces.values():
# Estimate armor decay (assuming it absorbs typical hit up to its protection)
armor_absorb = min(typical_hit, piece.protection.get_total())
total += piece.get_decay_for_damage(armor_absorb)
if piece.attached_plate:
# Estimate plate decay
plate_absorb = min(typical_hit, piece.attached_plate.get_total_protection())
total += piece.attached_plate.get_decay_for_damage(plate_absorb)
return total
def get_pieces_list(self) -> List[ArmorPiece]:
"""Get list of all pieces in slot order."""
return [self.pieces.get(slot) for slot in ALL_ARMOR_SLOTS if slot in self.pieces]
def to_dict(self) -> Dict:
"""Convert to dictionary."""
return {
@ -413,7 +413,7 @@ class ArmorSet:
'pieces': {slot.value: piece.to_dict() for slot, piece in self.pieces.items()},
'set_bonus': self.set_bonus.to_dict() if self.set_bonus else None,
}
@classmethod
def from_dict(cls, data: Dict) -> "ArmorSet":
"""Create from dictionary."""
@ -440,45 +440,45 @@ class EquippedArmor:
"""
# Individual pieces (mix & match)
pieces: Dict[ArmorSlot, ArmorPiece] = field(default_factory=dict)
# Or reference a full set
full_set: Optional[ArmorSet] = None
def get_piece(self, slot: ArmorSlot) -> Optional[ArmorPiece]:
"""Get equipped piece for a slot."""
if self.full_set:
return self.full_set.get_piece(slot)
return self.pieces.get(slot)
def get_all_pieces(self) -> Dict[ArmorSlot, ArmorPiece]:
"""Get all equipped pieces as a dict."""
if self.full_set:
return self.full_set.pieces
return self.pieces
def equip_piece(self, piece: ArmorPiece) -> None:
"""Equip an individual armor piece."""
# Unequip full set if equipping individual pieces
self.full_set = None
self.pieces[piece.slot] = piece
def unequip_piece(self, slot: ArmorSlot) -> Optional[ArmorPiece]:
"""Unequip a piece from a slot."""
if self.full_set:
return None # Can't unequip individual pieces from full set
return self.pieces.pop(slot, None)
def equip_full_set(self, armor_set: ArmorSet) -> None:
"""Equip a full armor set."""
self.full_set = armor_set
self.pieces = {}
def unequip_full_set(self) -> Optional[ArmorSet]:
"""Unequip full set."""
set_ref = self.full_set
self.full_set = None
return set_ref
def attach_plate_to_slot(self, slot: ArmorSlot, plate: ArmorPlate) -> bool:
"""Attach a plate to the armor piece in a slot."""
piece = self.get_piece(slot)
@ -486,29 +486,29 @@ class EquippedArmor:
piece.attach_plate(plate)
return True
return False
def remove_plate_from_slot(self, slot: ArmorSlot) -> Optional[ArmorPlate]:
"""Remove plate from armor piece in a slot."""
piece = self.get_piece(slot)
if piece:
return piece.remove_plate()
return None
def get_plate(self, slot: ArmorSlot) -> Optional[ArmorPlate]:
"""Get attached plate for a slot."""
piece = self.get_piece(slot)
return piece.attached_plate if piece else None
def get_total_protection(self) -> ProtectionProfile:
"""Get total protection from all equipped pieces and plates."""
if self.full_set:
return self.full_set.get_total_protection()
total = ProtectionProfile()
for piece in self.pieces.values():
total = total.add(piece.get_total_protection())
return total
def get_total_decay_per_hit(self) -> Decimal:
"""
Get total decay per hit (armor + plates).
@ -516,48 +516,48 @@ class EquippedArmor:
"""
if self.full_set:
return self.full_set.get_total_decay_per_hit()
# Estimate based on typical hit of 10 hp
typical_hit = Decimal("10")
total = Decimal("0")
for piece in self.pieces.values():
armor_absorb = min(typical_hit, piece.protection.get_total())
total += piece.get_decay_for_damage(armor_absorb)
if piece.attached_plate:
plate_absorb = min(typical_hit, piece.attached_plate.get_total_protection())
total += piece.attached_plate.get_decay_for_damage(plate_absorb)
return total
def get_coverage(self) -> Tuple[int, int]:
"""Get armor coverage as (equipped_slots, total_slots)."""
pieces = self.get_all_pieces()
return (len(pieces), 7)
def get_coverage_percentage(self) -> float:
"""Get armor coverage as percentage."""
equipped, total = self.get_coverage()
return (equipped / total) * 100 if total > 0 else 0
def get_slot_status(self) -> Dict[ArmorSlot, bool]:
"""Get status of each slot (True if equipped)."""
pieces = self.get_all_pieces()
return {slot: slot in pieces for slot in ALL_ARMOR_SLOTS}
def to_dict(self) -> Dict:
"""Convert to dictionary."""
return {
'pieces': {slot.value: piece.to_dict() for slot, piece in self.pieces.items()},
'full_set': self.full_set.to_dict() if self.full_set else None,
}
@classmethod
def from_dict(cls, data: Dict) -> "EquippedArmor":
"""Create from dictionary."""
equipped = cls()
if data.get('full_set'):
equipped.full_set = ArmorSet.from_dict(data['full_set'])
else:
@ -566,7 +566,7 @@ class EquippedArmor:
for slot, piece_data in data.get('pieces', {}).items()
}
equipped.pieces = pieces
return equipped
@ -579,17 +579,17 @@ class HitResult:
"""Result of a hit against armored target."""
raw_damage: Decimal
damage_type: str
# Damage absorbed
plate_absorbed: Decimal = Decimal("0")
armor_absorbed: Decimal = Decimal("0")
damage_to_avatar: Decimal = Decimal("0")
# Decay incurred
plate_decay: Decimal = Decimal("0")
armor_decay: Decimal = Decimal("0")
total_decay: Decimal = Decimal("0")
# Status
plate_broken: bool = False
armor_broken: bool = False
@ -603,7 +603,7 @@ def calculate_hit_protection(
) -> HitResult:
"""
Calculate damage absorption for a hit using Loot 2.0 mechanics.
Loot 2.0 Armor Mechanics (June 2017):
1. Plate absorbs damage FIRST (shield layer)
2. Armor absorbs remaining damage
@ -611,21 +611,21 @@ def calculate_hit_protection(
4. Armor decay = damage_absorbed_by_armor * armor.decay_per_hp
5. Decay is LINEAR per damage point (20 hp/pec standard = 0.05 pec/hp)
6. Block chance on upgraded plates can nullify hit (no decay)
Damage Flow:
Incoming Damage Plate absorbs first Armor absorbs remainder Player takes overflow
Example: 20 Impact hit vs Ghost Harness (4 Impact) + Impact Plate (3 Impact):
- Plate absorbs 3, decays for 3 * 0.05 = 0.15 PEC
- Armor absorbs 4 (remaining after plate), decays for 4 * 0.05 = 0.20 PEC
- Player takes 20 - 3 - 4 = 13 damage
Args:
equipped_armor: Currently equipped armor
incoming_damage: Raw damage from attack
damage_type: Type of damage (impact, burn, etc.)
hit_location: Specific slot hit (None for full body/average)
Returns:
HitResult with absorption details
"""
@ -633,10 +633,10 @@ def calculate_hit_protection(
raw_damage=incoming_damage,
damage_type=damage_type,
)
# Check for block chance on plates (upgraded plates only)
# This would nullify the hit completely with no decay
# Get protection for the hit
if hit_location:
# Specific location hit
@ -645,66 +645,66 @@ def calculate_hit_protection(
# No armor on that slot - full damage
result.damage_to_avatar = incoming_damage
return result
# Check for block on plate
if piece.attached_plate and piece.attached_plate.block_chance > 0:
# Note: In real implementation, use random() to check block
# For calculation purposes, we don't factor block chance
pass
# Plate protection for this damage type
plate_prot = Decimal("0")
if piece.attached_plate:
plate_prot = piece.attached_plate.get_effective_protection(damage_type)
# Armor protection for this damage type
armor_prot = piece.protection.get_effective_against(damage_type)
# Plate absorbs FIRST (up to its protection)
plate_absorb = min(plate_prot, incoming_damage)
result.plate_absorbed = plate_absorb
remaining = incoming_damage - plate_absorb
# Armor absorbs remainder (up to its protection)
armor_absorb = min(armor_prot, remaining)
result.armor_absorbed = armor_absorb
result.damage_to_avatar = remaining - armor_absorb
# Calculate decay based on actual damage absorbed (Loot 2.0)
if piece.attached_plate and plate_absorb > 0:
result.plate_decay = piece.attached_plate.get_decay_for_damage(plate_absorb)
if armor_absorb > 0:
result.armor_decay = piece.get_decay_for_damage(armor_absorb)
result.total_decay = result.plate_decay + result.armor_decay
else:
# Full body hit - use average protection from all equipped pieces
pieces = equipped_armor.get_all_pieces()
if not pieces:
result.damage_to_avatar = incoming_damage
return result
# Calculate total protection across all slots
total_plate_prot = Decimal("0")
total_armor_prot = Decimal("0")
for piece in pieces.values():
total_armor_prot += piece.protection.get_effective_against(damage_type)
if piece.attached_plate:
total_plate_prot += piece.attached_plate.get_effective_protection(damage_type)
# Plate absorbs FIRST
plate_absorb = min(total_plate_prot, incoming_damage)
result.plate_absorbed = plate_absorb
remaining = incoming_damage - plate_absorb
# Armor absorbs remainder
armor_absorb = min(total_armor_prot, remaining)
result.armor_absorbed = armor_absorb
result.damage_to_avatar = remaining - armor_absorb
# Calculate decay based on actual damage absorbed
# Distribute decay proportionally across all pieces
if plate_absorb > 0:
@ -715,7 +715,7 @@ def calculate_hit_protection(
# This plate's share of absorption
piece_plate_share = plate_absorb * (piece_plate_prot / total_plate_prot)
result.plate_decay += piece.attached_plate.get_decay_for_damage(piece_plate_share)
if armor_absorb > 0:
for piece in pieces.values():
piece_armor_prot = piece.protection.get_effective_against(damage_type)
@ -723,9 +723,9 @@ def calculate_hit_protection(
# This armor's share of absorption
piece_armor_share = armor_absorb * (piece_armor_prot / total_armor_prot)
result.armor_decay += piece.get_decay_for_damage(piece_armor_share)
result.total_decay = result.plate_decay + result.armor_decay
return result
@ -737,7 +737,7 @@ def create_ghost_set() -> ArmorSet:
"""Create the Ghost armor set (light, good vs cold/burn)."""
# Ghost: 2000 durability = 20.41 hp/pec
ghost_durability = 2000
pieces = {
ArmorSlot.HEAD: ArmorPiece(
name="Ghost Helmet",
@ -814,7 +814,7 @@ def create_shogun_set() -> ArmorSet:
"""Create the Shogun armor set (medium, good vs impact/cut)."""
# Shogun: 2500 durability = better economy than Ghost
shogun_durability = 2500
pieces = {
ArmorSlot.HEAD: ArmorPiece(
name="Shogun Helmet",
@ -890,7 +890,7 @@ def create_shogun_set() -> ArmorSet:
def create_vigilante_set() -> ArmorSet:
"""Create the Vigilante armor set (light, good all-around)."""
vigilante_durability = 2000 # Same as Ghost
pieces = {
ArmorSlot.HEAD: ArmorPiece(
name="Vigilante Helmet",
@ -966,7 +966,7 @@ def create_vigilante_set() -> ArmorSet:
def create_hermes_set() -> ArmorSet:
"""Create the Hermes armor set (medium, good vs penetration)."""
hermes_economy = Decimal("0.05")
pieces = {
ArmorSlot.HEAD: ArmorPiece(
name="Hermes Helmet",
@ -1043,7 +1043,7 @@ def create_pixie_set() -> ArmorSet:
"""Create the Pixie armor set (light starter armor)."""
# Pixie is starter armor, slightly worse economy (18 hp/pec)
pixie_economy = Decimal("0.055")
pieces = {
ArmorSlot.HEAD: ArmorPiece(
name="Pixie Helmet",
@ -1126,7 +1126,7 @@ def get_mock_plates() -> List[ArmorPlate]:
# Upgraded plates might have better economy (25 hp/pec = 0.04 pec per hp)
standard_economy = Decimal("0.05")
improved_economy = Decimal("0.04") # 25 hp/pec
return [
# General purpose plates
ArmorPlate(
@ -1226,7 +1226,7 @@ def create_frontier_set() -> ArmorSet:
"""Create the Frontier armor set (Arkadia mid-level, good mobility)."""
# Frontier Adjusted has 3800 durability = 20.83 hp/pec economy
frontier_economy = Decimal("0.048") # ~20.8 hp/pec
pieces = {
ArmorSlot.HEAD: ArmorPiece(
name="Frontier Helmet, Adjusted (M)",
@ -1237,17 +1237,17 @@ def create_frontier_set() -> ArmorSet:
protection=ProtectionProfile(impact=Decimal("4"), cut=Decimal("3"), stab=Decimal("3")),
weight=Decimal("0.5"),
),
ArmorSlot.CHEST: ArmorPiece(
ArmorSlot.TORSO: ArmorPiece(
name="Frontier Harness, Adjusted (M)",
item_id="frontier_harness_adj_m",
slot=ArmorSlot.CHEST,
slot=ArmorSlot.TORSO,
set_name="Frontier",
decay_per_hp=frontier_economy,
protection=ProtectionProfile(impact=Decimal("8"), cut=Decimal("6"), stab=Decimal("6")),
weight=Decimal("1.0"),
),
ArmorSlot.ARMS: ArmorPiece(
name="Frontier Arm Guards, Adjusted (M)", # Both arms
name="Frontier Arm Guards, Adjusted (M)",
item_id="frontier_arm_adj_m",
slot=ArmorSlot.ARMS,
set_name="Frontier",
@ -1256,7 +1256,7 @@ def create_frontier_set() -> ArmorSet:
weight=Decimal("0.8"),
),
ArmorSlot.HANDS: ArmorPiece(
name="Frontier Gloves, Adjusted (M)", # Both hands
name="Frontier Gloves, Adjusted (M)",
item_id="frontier_gloves_adj_m",
slot=ArmorSlot.HANDS,
set_name="Frontier",
@ -1264,10 +1264,10 @@ def create_frontier_set() -> ArmorSet:
protection=ProtectionProfile(impact=Decimal("3"), cut=Decimal("2"), stab=Decimal("2")),
weight=Decimal("0.6"),
),
ArmorSlot.THIGHS: ArmorPiece(
ArmorSlot.LEGS: ArmorPiece(
name="Frontier Thigh Guards, Adjusted (M)",
item_id="frontier_thigh_adj_m",
slot=ArmorSlot.THIGHS,
slot=ArmorSlot.LEGS,
set_name="Frontier",
decay_per_hp=frontier_economy,
protection=ProtectionProfile(impact=Decimal("4"), cut=Decimal("3"), stab=Decimal("3")),
@ -1340,16 +1340,16 @@ def get_pieces_by_set(set_name: str) -> List[ArmorPiece]:
def create_mixed_armor(selections: Dict[ArmorSlot, str]) -> EquippedArmor:
"""
Create mixed armor from piece names.
Args:
selections: Dict mapping slots to piece names
Returns:
EquippedArmor with mixed pieces
"""
equipped = EquippedArmor()
all_pieces = {p.name.lower(): p for p in get_all_armor_pieces()}
for slot, piece_name in selections.items():
piece_key = piece_name.lower()
if piece_key in all_pieces:
@ -1376,7 +1376,7 @@ def create_mixed_armor(selections: Dict[ArmorSlot, str]) -> EquippedArmor:
weight=original.weight,
)
equipped.equip_piece(piece_copy)
return equipped