import 'package:flutter/foundation.dart'; import 'package:asciineverdie/src/core/model/equipment_item.dart'; import 'package:asciineverdie/src/core/model/equipment_slot.dart'; import 'package:asciineverdie/src/core/model/game_state.dart'; import 'package:asciineverdie/src/core/model/item_stats.dart'; /// 플레이어 사망 처리 서비스 /// /// ProgressService에서 분리된 사망 관련 로직 담당: /// - 장비 손실 계산 /// - 사망 정보 기록 /// - 보스전 레벨링 모드 진입 class DeathHandler { const DeathHandler(); /// 플레이어 사망 처리 (Phase 4) /// /// 모든 장비 상실 및 사망 정보 기록. /// 보스전 사망 시: 장비 보호 + 5분 레벨링 모드 진입. GameState processPlayerDeath( GameState state, { required String killerName, required DeathCause cause, }) { // 사망 직전 전투 이벤트 저장 (최대 10개) final lastCombatEvents = state.progress.currentCombat?.recentEvents ?? const []; // 보스전 사망 여부 확인 (최종 보스 fighting 상태) final isBossDeath = state.progress.finalBossState == FinalBossState.fighting; // 보스전 사망이 아닐 경우에만 장비 손실 var newEquipment = state.equipment; var lostCount = 0; String? lostItemName; EquipmentSlot? lostItemSlot; ItemRarity? lostItemRarity; EquipmentItem? lostEquipmentItem; // 광고 부활 시 복구용 if (!isBossDeath) { final lossResult = _calculateEquipmentLoss(state); newEquipment = lossResult.equipment; lostCount = lossResult.lostCount; lostItemName = lossResult.lostItemName; lostItemSlot = lossResult.lostItemSlot; lostItemRarity = lossResult.lostItemRarity; lostEquipmentItem = lossResult.lostItem; } // 사망 정보 생성 (전투 로그 포함) final deathInfo = DeathInfo( cause: cause, killerName: killerName, lostEquipmentCount: lostCount, lostItemName: lostItemName, lostItemSlot: lostItemSlot, lostItemRarity: lostItemRarity, lostItem: lostEquipmentItem, goldAtDeath: state.inventory.gold, levelAtDeath: state.traits.level, timestamp: state.skillSystem.elapsedMs, lastCombatEvents: lastCombatEvents, ); // 보스전 사망 시 5분 레벨링 모드 진입 final bossLevelingEndTime = isBossDeath ? DateTime.now().millisecondsSinceEpoch + (5 * 60 * 1000) // 5분 : null; // 전투 상태 초기화 및 사망 횟수 증가 final progress = state.progress.copyWith( currentCombat: null, deathCount: state.progress.deathCount + 1, bossLevelingEndTime: bossLevelingEndTime, ); return state.copyWith( equipment: newEquipment, progress: progress, deathInfo: deathInfo, ); } /// 장비 손실 계산 ({ Equipment equipment, int lostCount, String? lostItemName, EquipmentSlot? lostItemSlot, ItemRarity? lostItemRarity, EquipmentItem? lostItem, }) _calculateEquipmentLoss(GameState state) { var newEquipment = state.equipment; // 레벨 기반 장비 손실 확률 계산 // Lv 1: 20%, Lv 5: ~56%, Lv 10+: 100% // 공식: 20 + (level - 1) * 80 / 9 final level = state.traits.level; final lossChancePercent = level >= 10 ? 100 : (20 + ((level - 1) * 80 ~/ 9)).clamp(0, 100); final roll = state.rng.nextInt(100); // 0~99 final shouldLoseEquipment = roll < lossChancePercent; debugPrint( '[Death] Lv$level lossChance=$lossChancePercent% roll=$roll ' 'shouldLose=$shouldLoseEquipment', ); if (!shouldLoseEquipment) { return ( equipment: newEquipment, lostCount: 0, lostItemName: null, lostItemSlot: null, lostItemRarity: null, lostItem: null, ); } // 무기(슬롯 0)를 제외한 장착된 장비 중 1개를 제물로 삭제 final equippedNonWeaponSlots = []; for (var i = 1; i < Equipment.slotCount; i++) { final item = state.equipment.getItemByIndex(i); if (item.isNotEmpty) { equippedNonWeaponSlots.add(i); } } if (equippedNonWeaponSlots.isEmpty) { return ( equipment: newEquipment, lostCount: 0, lostItemName: null, lostItemSlot: null, lostItemRarity: null, lostItem: null, ); } // 랜덤하게 1개 슬롯 선택 final sacrificeIndex = equippedNonWeaponSlots[state.rng.nextInt( equippedNonWeaponSlots.length, )]; // 제물로 바칠 아이템 정보 저장 final lostItem = state.equipment.getItemByIndex(sacrificeIndex); final lostItemSlot = EquipmentSlot.values[sacrificeIndex]; // 해당 슬롯을 빈 장비로 교체 newEquipment = newEquipment.setItemByIndex( sacrificeIndex, EquipmentItem.empty(lostItemSlot), ); debugPrint('[Death] Lost item: ${lostItem.name} (slot: $lostItemSlot)'); return ( equipment: newEquipment, lostCount: 1, lostItemName: lostItem.name, lostItemSlot: lostItemSlot, lostItemRarity: lostItem.rarity, lostItem: lostItem, ); } }