""" Status effect utilities for display and management. """ from collections import defaultdict def stack_status_effects(effects: list) -> dict: """ Stack status effects by name, summing damage and counting stacks. Args: effects: List of dicts with keys: effect_name, effect_icon, damage_per_tick, ticks_remaining Returns: Dict with keys: effect_name -> {icon, total_damage, stacks, min_ticks, effects: [list of effect dicts]} """ stacked = defaultdict(lambda: { 'icon': '', 'total_damage': 0, 'stacks': 0, 'min_ticks': float('inf'), 'max_ticks': 0, 'effects': [] }) for effect in effects: name = effect['effect_name'] stacked[name]['icon'] = effect['effect_icon'] stacked[name]['total_damage'] += effect.get('damage_per_tick', 0) stacked[name]['stacks'] += 1 stacked[name]['min_ticks'] = min(stacked[name]['min_ticks'], effect['ticks_remaining']) stacked[name]['max_ticks'] = max(stacked[name]['max_ticks'], effect['ticks_remaining']) stacked[name]['effects'].append(effect) return dict(stacked) def get_status_summary(effects: list, in_combat: bool = False) -> str: """ Generate compact status summary for display in menus. Args: effects: List of status effect dicts in_combat: If True, show "turns" instead of "cycles" Returns: String like "Statuses: 🩸 (-4), ☣️ (-3)" or empty string if no effects """ if not effects: return "" stacked = stack_status_effects(effects) if not stacked: return "" parts = [] for name, data in stacked.items(): if data['total_damage'] > 0: parts.append(f"{data['icon']} (-{data['total_damage']})") else: parts.append(f"{data['icon']}") return "Statuses: " + ", ".join(parts) def get_status_details(effects: list, in_combat: bool = False) -> str: """ Generate detailed status display for profile menu. Args: effects: List of status effect dicts in_combat: If True, show "turns" instead of "cycles" Returns: Multi-line string with detailed effect info """ if not effects: return "No active status effects." stacked = stack_status_effects(effects) lines = [] for name, data in stacked.items(): # Build effect line effect_line = f"{data['icon']} {name.replace('_', ' ').title()}" # Add damage info if data['total_damage'] > 0: effect_line += f": -{data['total_damage']} HP/{'turn' if in_combat else 'cycle'}" # Add tick info if data['stacks'] == 1: tick_unit = 'turn' if in_combat else 'cycle' tick_count = data['min_ticks'] effect_line += f" ({tick_count} {tick_unit}{'s' if tick_count != 1 else ''} left)" else: tick_unit = 'turns' if in_combat else 'cycles' if data['min_ticks'] == data['max_ticks']: effect_line += f" (×{data['stacks']}, {data['min_ticks']} {tick_unit} left)" else: effect_line += f" (×{data['stacks']}, {data['min_ticks']}-{data['max_ticks']} {tick_unit} left)" lines.append(effect_line) return "\n".join(lines) def calculate_status_damage(effects: list) -> int: """ Calculate total damage from all status effects. Args: effects: List of status effect dicts Returns: Total damage per tick """ return sum(effect.get('damage_per_tick', 0) for effect in effects)