diff --git a/core/armor_system.py b/core/armor_system.py index 18ae6b9..815e1c7 100644 --- a/core/armor_system.py +++ b/core/armor_system.py @@ -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