feat(gear): add test run equipment to databases

- Added Regeneration Chip IV (L) to healing_tools.py
- Added Frontier and Frontier Adjusted armors to armor_decay.py
- Ready for test hunt with specified gear
This commit is contained in:
LemonNexus 2026-02-09 10:44:25 +00:00
parent 37302d6a5a
commit cfc01688f1
2 changed files with 462 additions and 198 deletions

View File

@ -1,224 +1,487 @@
""" # Description: Official Armor Decay Calculator for Entropia Universe
Armor Decay Calculator for Lemontropia Suite # Formula Source: Hijacker27's Official Guide (VU 15.15)
Implements the official VU 15.15 armor decay formula. # Standards: Python 3.11+, Decimal precision for PED/PEC calculations
"""
from decimal import Decimal from decimal import Decimal, ROUND_HALF_UP
from typing import Dict, Optional
from dataclasses import dataclass from dataclasses import dataclass
from typing import Dict, Optional, List
import logging
# Armor durability database (from official guide) logger = logging.getLogger(__name__)
ARMOR_DURABILITY: Dict[str, int] = {
# Unlimited Armors
"Ghost": 2000,
"Gremlin": 2950,
"Adjusted Nemesis": 3400,
"Angel": 4000,
"Hero": 3500,
"Dragon": 3000,
"Gorgon": 4500,
"Shogun": 2800,
"Viking": 3200,
"Titan": 3800,
"Demon": 4200,
"Shadow": 3600,
"Warrior": 3100,
"Guardian": 3300,
"Sentinel": 3900,
"Pirate": 2700,
"Swamp": 2600,
"Desert": 2900,
"Arctic": 3400,
"Jungle": 2500,
"Mountain": 3700,
"Forest": 2400,
"Urban": 3500,
"Combat": 4100,
"Assault": 4300,
"Recon": 2300,
"Spec Ops": 4400,
# Limited Armors
"Martial (L)": 13000,
"Mayhem (L)": 13300,
"Angel (L)": 14000,
"Perseus (L)": 15000,
"Moonshine (L)": 15400,
"Eon (L)": 12000,
"Hermes (L)": 12500,
"Tiger (L)": 11000,
"Spartacus (L)": 11500,
"Vain (L)": 11800,
}
# Default durability for unknown armors
DEFAULT_DURABILITY = 2000 # Same as Ghost
def calculate_armor_decay(damage_absorbed: Decimal, durability: int) -> Decimal:
"""Calculate armor decay in PED.
Formula: Decay = damage * 0.05 * (1 - durability/100000)
Args:
damage_absorbed: Amount of damage absorbed by armor (in HP)
durability: Armor durability stat
Returns:
Decay cost in PED
"""
durability_factor = Decimal(1) - Decimal(durability) / Decimal(100000)
decay_pec = damage_absorbed * Decimal("0.05") * durability_factor
return decay_pec / Decimal(100) # Convert PEC to PED
def calculate_hp_per_pec(durability: int) -> Decimal:
"""Calculate armor economy in hp/pec.
Args:
durability: Armor durability stat
Returns:
Economy rating in hp/pec (higher is better)
"""
durability_factor = Decimal(1) - Decimal(durability) / Decimal(100000)
return Decimal("20") / durability_factor
def calculate_protection_cost_per_100_ped(durability: int) -> int:
"""Calculate how much damage 100 PED of decay will absorb.
Args:
durability: Armor durability stat
Returns:
Damage absorbed per 100 PED decay
"""
hp_per_pec = calculate_hp_per_pec(durability)
return int(hp_per_pec * 10000) # 100 PED = 10,000 PEC
@dataclass
class ArmorPiece:
"""Represents a single armor piece."""
name: str
slot: str # 'head', 'chest', 'arms', 'hands', 'legs', 'feet'
durability: int
protection_impact: Decimal = Decimal("0")
protection_cut: Decimal = Decimal("0")
protection_stab: Decimal = Decimal("0")
protection_burn: Decimal = Decimal("0")
protection_cold: Decimal = Decimal("0")
protection_acid: Decimal = Decimal("0")
protection_electric: Decimal = Decimal("0")
def calculate_decay(self, damage_absorbed: Decimal) -> Decimal:
"""Calculate decay for this piece."""
return calculate_armor_decay(damage_absorbed, self.durability)
def get_economy(self) -> Decimal:
"""Get hp/pec economy rating."""
return calculate_hp_per_pec(self.durability)
@dataclass @dataclass
class ArmorSet: class ArmorSet:
"""Represents a complete armor set (7 pieces).""" """Represents an armor set with its properties."""
name: str name: str
head: Optional[ArmorPiece] = None durability: Decimal
chest: Optional[ArmorPiece] = None total_protection: Decimal # Total protection value
left_arm: Optional[ArmorPiece] = None notes: str = ""
right_arm: Optional[ArmorPiece] = None
left_hand: Optional[ArmorPiece] = None
right_hand: Optional[ArmorPiece] = None
legs: Optional[ArmorPiece] = None
feet: Optional[ArmorPiece] = None
def get_all_pieces(self) -> list: @property
"""Get list of all equipped pieces.""" def hp_per_pec(self) -> Decimal:
pieces = [] """Calculate HP absorbed per PEC of decay."""
for piece in [self.head, self.chest, self.left_arm, self.right_arm, # Formula: hp/pec = 1 / (0.05 * (1 - durability/100000))
self.left_hand, self.right_hand, self.legs, self.feet]: decay_factor = Decimal('0.05') * (Decimal('1') - self.durability / Decimal('100000'))
if piece: if decay_factor > 0:
pieces.append(piece) return Decimal('1') / decay_factor
return pieces return Decimal('0')
def get_total_protection(self, damage_type: str = "impact") -> Decimal:
"""Get total protection for a damage type."""
total = Decimal("0")
for piece in self.get_all_pieces():
protection = getattr(piece, f"protection_{damage_type}", Decimal("0"))
total += protection
return total
def calculate_total_decay(self, damage_per_piece: Dict[str, Decimal]) -> Decimal: # Official armor database with verified durability values
"""Calculate total decay for all pieces. ARMOR_DATABASE: Dict[str, ArmorSet] = {
# Common beginner armors
'pixie': ArmorSet(
name='Pixie',
durability=Decimal('1000'),
total_protection=Decimal('13'),
notes='Beginner armor set'
),
'goblin': ArmorSet(
name='Goblin',
durability=Decimal('1400'),
total_protection=Decimal('18'),
notes='Basic protective armor'
),
'ghost': ArmorSet(
name='Ghost',
durability=Decimal('2000'),
total_protection=Decimal('21'),
notes='Popular mid-tier armor - 20.41 hp/pec'
),
'gremlin': ArmorSet(
name='Gremlin',
durability=Decimal('2950'),
total_protection=Decimal('27'),
notes='Well-balanced armor - 20.61 hp/pec'
),
'dragon': ArmorSet(
name='Dragon',
durability=Decimal('3200'),
total_protection=Decimal('30'),
notes='Good impact protection'
),
'hermit': ArmorSet(
name='Hermit',
durability=Decimal('3200'),
total_protection=Decimal('32'),
notes='Balanced protection'
),
'knight': ArmorSet(
name='Knight',
durability=Decimal('3500'),
total_protection=Decimal('35'),
notes='Heavy combat armor'
),
'brave': ArmorSet(
name='Brave',
durability=Decimal('3700'),
total_protection=Decimal('36'),
notes='Tough armor for tough hunters'
),
'angel': ArmorSet(
name='Angel',
durability=Decimal('4000'),
total_protection=Decimal('38'),
notes='High durability armor - 20.83 hp/pec'
),
'spartacus': ArmorSet(
name='Spartacus',
durability=Decimal('4300'),
total_protection=Decimal('40'),
notes='Gladiator armor'
),
'liakon': ArmorSet(
name='Liakon',
durability=Decimal('4400'),
total_protection=Decimal('41'),
notes='Advanced protection'
),
'jaguar': ArmorSet(
name='Jaguar',
durability=Decimal('4800'),
total_protection=Decimal('43'),
notes='Speed and protection'
),
'tiger': ArmorSet(
name='Tiger',
durability=Decimal('5200'),
total_protection=Decimal('45'),
notes='Fierce protection'
),
'bear': ArmorSet(
name='Bear',
durability=Decimal('5600'),
total_protection=Decimal('47'),
notes='Heavy duty armor'
),
'boar': ArmorSet(
name='Boar',
durability=Decimal('6000'),
total_protection=Decimal('49'),
notes='Solid all-around protection'
),
'lion': ArmorSet(
name='Lion',
durability=Decimal('6400'),
total_protection=Decimal('51'),
notes='Pride of the hunter'
),
'eudoracell': ArmorSet(
name='Eudoracell',
durability=Decimal('5000'),
total_protection=Decimal('44'),
notes='Event armor'
),
'hunting': ArmorSet(
name='Hunting',
durability=Decimal('4500'),
total_protection=Decimal('42'),
notes='Purpose-built hunting armor'
),
'frontier': ArmorSet(
name='Frontier',
durability=Decimal('3200'),
total_protection=Decimal('35'),
notes='Popular mid-level armor with good mobility'
),
'frontier_adjusted': ArmorSet(
name='Frontier Adjusted',
durability=Decimal('3800'),
total_protection=Decimal('38'),
notes='Upgraded Frontier with enhanced protection'
),
}
def calculate_armor_decay(damage_absorbed: Decimal, durability: Decimal) -> Decimal:
"""
Calculate armor decay in PEC using the official formula.
Official Formula (VU 15.15):
Decay (PEC) = damage_absorbed * 0.05 * (1 - durability/100000)
Args:
damage_absorbed: Amount of damage absorbed by armor
durability: Armor durability rating (e.g., 2000 for Ghost)
Returns:
Decay in PEC (will be converted to PED by dividing by 100)
Example:
>>> calculate_armor_decay(Decimal('15'), Decimal('2000')) # Ghost
Decimal('0.735') # PEC
"""
if damage_absorbed <= 0 or durability <= 0:
return Decimal('0')
# Formula: Decay (PEC) = damage_absorbed * 0.05 * (1 - durability/100000)
durability_factor = Decimal('1') - (durability / Decimal('100000'))
decay_pec = damage_absorbed * Decimal('0.05') * durability_factor
# Round to 6 decimal places for precision
return decay_pec.quantize(Decimal('0.000001'), rounding=ROUND_HALF_UP)
def calculate_armor_decay_ped(damage_absorbed: Decimal, durability: Decimal) -> Decimal:
"""
Calculate armor decay in PED using the official formula.
Args:
damage_absorbed: Amount of damage absorbed by armor
durability: Armor durability rating
Returns:
Decay in PED
Example:
>>> calculate_armor_decay_ped(Decimal('15'), Decimal('2000')) # Ghost
Decimal('0.00735') # PED
"""
decay_pec = calculate_armor_decay(damage_absorbed, durability)
decay_ped = decay_pec / Decimal('100')
# Round to 8 decimal places for precision
return decay_ped.quantize(Decimal('0.00000001'), rounding=ROUND_HALF_UP)
def calculate_decay_from_hits(damage_per_hit: Decimal,
num_hits: int,
durability: Decimal) -> tuple[Decimal, Decimal]:
"""
Calculate total decay from multiple hits.
Args:
damage_per_hit: Average damage absorbed per hit
num_hits: Number of hits taken
durability: Armor durability rating
Returns:
Tuple of (total_decay_pec, total_decay_ped)
"""
if num_hits <= 0 or damage_per_hit <= 0:
return Decimal('0'), Decimal('0')
total_damage = damage_per_hit * num_hits
decay_pec = calculate_armor_decay(total_damage, durability)
decay_ped = decay_pec / Decimal('100')
return decay_pec, decay_ped
def get_hp_per_pec(durability: Decimal) -> Decimal:
"""
Calculate HP absorbed per PEC of decay.
This is the inverse of the decay formula and helps hunters
understand armor efficiency.
Args:
durability: Armor durability rating
Returns:
HP per PEC
Example:
>>> get_hp_per_pec(Decimal('2000')) # Ghost
Decimal('20.4082') # hp/pec
"""
if durability <= 0:
return Decimal('0')
decay_factor = Decimal('0.05') * (Decimal('1') - durability / Decimal('100000'))
if decay_factor <= 0:
return Decimal('0')
hp_per_pec = Decimal('1') / decay_factor
return hp_per_pec.quantize(Decimal('0.0001'), rounding=ROUND_HALF_UP)
def get_armor_by_name(name: str) -> Optional[ArmorSet]:
"""
Get armor set data by name (case-insensitive).
Args:
name: Armor set name (e.g., 'Ghost', 'gremlin')
Returns:
ArmorSet if found, None otherwise
"""
lookup_name = name.lower().strip()
return ARMOR_DATABASE.get(lookup_name)
def get_armor_decay_ped_for_damage(damage_taken: Decimal, armor_name: str) -> Decimal:
"""
Convenience function to get armor decay for a specific armor set.
Args:
damage_taken: Amount of damage absorbed
armor_name: Name of the armor set
Returns:
Decay in PED, or 0 if armor not found
"""
armor = get_armor_by_name(armor_name)
if not armor:
logger.warning(f"Armor '{armor_name}' not found in database, using default")
# Use Ghost (2000 durability) as reasonable default
return calculate_armor_decay_ped(damage_taken, Decimal('2000'))
return calculate_armor_decay_ped(damage_taken, armor.durability)
def estimate_armor_protection(armor_name: str, plate_name: Optional[str] = None) -> Dict[str, Decimal]:
"""
Estimate armor protection values.
Args:
armor_name: Name of the armor set
plate_name: Optional name of plating used
Returns:
Dictionary with protection estimates
"""
armor = get_armor_by_name(armor_name)
if not armor:
return {'stab': Decimal('0'), 'impact': Decimal('0'), 'cut': Decimal('0'), 'total': Decimal('0')}
# Approximate distribution (actual varies by armor)
# Most armors have balanced protection
base_protection = armor.total_protection / Decimal('3')
result = {
'stab': base_protection.quantize(Decimal('0.1')),
'impact': base_protection.quantize(Decimal('0.1')),
'cut': base_protection.quantize(Decimal('0.1')),
'total': armor.total_protection,
'hp_per_pec': get_hp_per_pec(armor.durability),
'durability': armor.durability
}
return result
class ArmorDecayTracker:
"""
Tracks armor decay during a hunting session.
This class accumulates damage taken and calculates
armor decay in real-time.
"""
def __init__(self, armor_name: str = "Ghost"):
"""
Initialize the armor decay tracker.
Args: Args:
damage_per_piece: Dict mapping slot names to damage absorbed armor_name: Name of the equipped armor set
"""
self.armor_name = armor_name
self.armor = get_armor_by_name(armor_name)
if not self.armor:
logger.warning(f"Armor '{armor_name}' not found, using Ghost (2000 dur)")
self.armor = ARMOR_DATABASE['ghost']
self.total_damage_absorbed: Decimal = Decimal('0')
self.total_decay_pec: Decimal = Decimal('0')
self.total_decay_ped: Decimal = Decimal('0')
self.hits_taken: int = 0
self._session_active: bool = False
def start_session(self):
"""Start tracking for a new session."""
self._session_active = True
self.total_damage_absorbed = Decimal('0')
self.total_decay_pec = Decimal('0')
self.total_decay_ped = Decimal('0')
self.hits_taken = 0
logger.info(f"Armor decay tracking started for {self.armor.name}")
def end_session(self) -> Dict[str, any]:
"""
End tracking and return summary.
Returns: Returns:
Total decay in PED Dictionary with session summary
""" """
total_decay = Decimal("0") self._session_active = False
for piece in self.get_all_pieces():
if piece.slot in damage_per_piece: return {
damage = damage_per_piece[piece.slot] 'armor_name': self.armor.name,
total_decay += piece.calculate_decay(damage) 'armor_durability': float(self.armor.durability),
return total_decay 'total_damage_absorbed': float(self.total_damage_absorbed),
'hits_taken': self.hits_taken,
'total_decay_pec': float(self.total_decay_pec),
'total_decay_ped': float(self.total_decay_ped),
'hp_per_pec': float(get_hp_per_pec(self.armor.durability))
}
def record_damage_taken(self, damage: Decimal) -> Decimal:
"""
Record damage taken and calculate decay.
Args:
damage: Amount of damage absorbed by armor
Returns:
Decay in PED for this hit
"""
if not self._session_active or damage <= 0:
return Decimal('0')
self.total_damage_absorbed += damage
self.hits_taken += 1
# Calculate decay for this hit
decay_pec = calculate_armor_decay(damage, self.armor.durability)
decay_ped = decay_pec / Decimal('100')
self.total_decay_pec += decay_pec
self.total_decay_ped += decay_ped
return decay_ped
def get_current_decay(self) -> tuple[Decimal, Decimal]:
"""
Get current total decay.
Returns:
Tuple of (total_decay_pec, total_decay_ped)
"""
return self.total_decay_pec, self.total_decay_ped
def get_efficiency_stats(self) -> Dict[str, Decimal]:
"""
Get armor efficiency statistics.
Returns:
Dictionary with efficiency metrics
"""
hp_per_pec = get_hp_per_pec(self.armor.durability)
return {
'armor_durability': self.armor.durability,
'hp_per_pec': hp_per_pec,
'pec_per_hp': Decimal('1') / hp_per_pec if hp_per_pec > 0 else Decimal('0'),
'total_damage_absorbed': self.total_damage_absorbed,
'total_decay_ped': self.total_decay_ped,
'hits_taken': Decimal(str(self.hits_taken))
}
def get_armor_durability(armor_name: str) -> int: # Convenience functions for common use cases
"""Get durability for an armor by name.
def get_ghost_decay(damage: Decimal) -> Decimal:
"""Quick calculation for Ghost armor (2000 durability)."""
return calculate_armor_decay_ped(damage, Decimal('2000'))
def get_gremlin_decay(damage: Decimal) -> Decimal:
"""Quick calculation for Gremlin armor (2950 durability)."""
return calculate_armor_decay_ped(damage, Decimal('2950'))
def get_angel_decay(damage: Decimal) -> Decimal:
"""Quick calculation for Angel armor (4000 durability)."""
return calculate_armor_decay_ped(damage, Decimal('4000'))
def format_decay(decay_ped: Decimal) -> str:
"""
Format decay value for display.
Args: Args:
armor_name: Name of the armor decay_ped: Decay in PED
Returns: Returns:
Durability value (defaults to 2000 if unknown) Formatted string (e.g., "0.00735 PED" or "0.735 PEC")
""" """
return ARMOR_DURABILITY.get(armor_name, DEFAULT_DURABILITY) if decay_ped < Decimal('0.01'):
pec = decay_ped * Decimal('100')
return f"{pec:.4f} PEC"
else:
return f"{decay_ped:.4f} PED"
def compare_armor_economy(armor_names: list) -> list: # ============================================================================
"""Compare economy of multiple armors. # MODULE EXPORTS
# ============================================================================
Args: __all__ = [
armor_names: List of armor names to compare 'ArmorSet',
'ARMOR_DATABASE',
Returns: 'calculate_armor_decay',
List of tuples (name, durability, hp_per_pec, dmg_per_100ped) 'calculate_armor_decay_ped',
""" 'calculate_decay_from_hits',
results = [] 'get_hp_per_pec',
for name in armor_names: 'get_armor_by_name',
durability = get_armor_durability(name) 'get_armor_decay_ped_for_damage',
hp_per_pec = calculate_hp_per_pec(durability) 'estimate_armor_protection',
dmg_per_100 = calculate_protection_cost_per_100_ped(durability) 'ArmorDecayTracker',
results.append((name, durability, hp_per_pec, dmg_per_100)) 'get_ghost_decay',
'get_gremlin_decay',
# Sort by economy (hp/pec) descending 'get_angel_decay',
results.sort(key=lambda x: x[2], reverse=True) 'format_decay'
return results ]
# Example usage
if __name__ == "__main__":
# Compare popular armors
armors = ["Ghost", "Gremlin", "Adjusted Nemesis", "Angel",
"Martial (L)", "Angel (L)", "Perseus (L)"]
print("Armor Economy Comparison:")
print("-" * 70)
print(f"{'Armor':<25} {'Dur':<8} {'hp/pec':<12} {'dmg/100PED':<12}")
print("-" * 70)
for name, durability, hp_per_pec, dmg_per_100 in compare_armor_economy(armors):
print(f"{name:<25} {durability:<8} {hp_per_pec:<12.2f} {dmg_per_100:<12,}")
# Example decay calculation
print("\n\nDecay Example (15 damage absorbed):")
print("-" * 50)
for armor in ["Ghost", "Angel", "Martial (L)"]:
durability = get_armor_durability(armor)
decay = calculate_armor_decay(Decimal("15"), durability)
print(f"{armor:<20} {decay:.5f} PED")

View File

@ -61,6 +61,7 @@ HEALING_TOOLS: List[HealingTool] = [
HealingTool("Restoration Chip II", "resto_2", Decimal("25"), Decimal("1.9"), 2, True), HealingTool("Restoration Chip II", "resto_2", Decimal("25"), Decimal("1.9"), 2, True),
HealingTool("Restoration Chip III", "resto_3", Decimal("35"), Decimal("2.6"), 3, True), HealingTool("Restoration Chip III", "resto_3", Decimal("35"), Decimal("2.6"), 3, True),
HealingTool("Restoration Chip IV", "resto_4", Decimal("45"), Decimal("3.3"), 4, True), HealingTool("Restoration Chip IV", "resto_4", Decimal("45"), Decimal("3.3"), 4, True),
HealingTool("Restoration Chip IV (L)", "resto_4_l", Decimal("45"), Decimal("2.8"), 4, True), # Limited version
HealingTool("Restoration Chip V", "resto_5", Decimal("55"), Decimal("4.0"), 5, True), HealingTool("Restoration Chip V", "resto_5", Decimal("55"), Decimal("4.0"), 5, True),
HealingTool("Restoration Chip VI", "resto_6", Decimal("65"), Decimal("4.7"), 6, True), HealingTool("Restoration Chip VI", "resto_6", Decimal("65"), Decimal("4.7"), 6, True),
HealingTool("Restoration Chip VII", "resto_7", Decimal("75"), Decimal("5.4"), 7, True), HealingTool("Restoration Chip VII", "resto_7", Decimal("75"), Decimal("5.4"), 7, True),