Files
asciinevrdie/lib/src/core/engine/death_handler.dart
JiWoong Sul c4280c929d fix: Critical 버그 3건 수정
- death_handler: print() → debugPrint()로 변경 (프로덕션 성능)
- arena_combat_simulator: DOT 스킬 INT/WIS 실제 스탯 사용
- ad_service: iOS 광고 ID 플레이스홀더를 테스트 ID로 대체
2026-03-19 14:48:14 +09:00

173 lines
5.1 KiB
Dart

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 = <int>[];
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,
);
}
}