feat(death): Phase 4 사망/부활 시스템 구현
- DeathInfo, DeathCause 클래스 정의 (game_state.dart) - 사망 원인, 상실 장비 수, 사망 시점 정보 기록 - ShopService 구현 (shop_service.dart) - 장비 가격 계산 (레벨 * 50 * 희귀도 배율) - 슬롯별 장비 생성 (프로그래밍 테마) - 자동 구매 (빈 슬롯에 Common 장비) - ResurrectionService 구현 (resurrection_service.dart) - 사망 처리: 모든 장비 상실, 기본 무기만 유지 - 부활 처리: HP/MP 회복, 자동 장비 구매 - progress_service.dart 사망 판정 로직 추가 - 전투 중 HP <= 0 시 사망 처리 - ProgressTickResult에 playerDied 플래그 추가 - progress_loop.dart 사망 시 루프 정지 - onPlayerDied 콜백 추가 - 사망 상태에서 틱 진행 방지 - DeathOverlay 위젯 구현 (death_overlay.dart) - ASCII 스컬 아트, 사망 원인, 상실 정보 표시 - 부활 버튼 - GameSessionController 사망/부활 상태 관리 - GameSessionStatus.dead 상태 추가 - resurrect() 메서드로 부활 처리
This commit is contained in:
182
lib/src/core/engine/resurrection_service.dart
Normal file
182
lib/src/core/engine/resurrection_service.dart
Normal file
@@ -0,0 +1,182 @@
|
||||
import 'package:askiineverdie/src/core/engine/shop_service.dart';
|
||||
import 'package:askiineverdie/src/core/model/equipment_item.dart';
|
||||
import 'package:askiineverdie/src/core/model/equipment_slot.dart';
|
||||
import 'package:askiineverdie/src/core/model/game_state.dart';
|
||||
|
||||
/// 부활 시스템 서비스 (Phase 4)
|
||||
///
|
||||
/// 사망 처리, 부활 처리, 장비 상실 등을 담당
|
||||
class ResurrectionService {
|
||||
const ResurrectionService({required this.shopService});
|
||||
|
||||
final ShopService shopService;
|
||||
|
||||
// ============================================================================
|
||||
// 사망 처리
|
||||
// ============================================================================
|
||||
|
||||
/// 플레이어 사망 처리
|
||||
///
|
||||
/// 1. 모든 장비 제거 (인벤토리로 이동하지 않음 - 상실)
|
||||
/// 2. 전투 상태 초기화
|
||||
/// 3. 사망 정보 기록
|
||||
GameState processDeath({
|
||||
required GameState state,
|
||||
required String killerName,
|
||||
required DeathCause cause,
|
||||
}) {
|
||||
// 상실할 장비 개수 계산
|
||||
final lostCount = state.equipment.equippedItems.length;
|
||||
|
||||
// 빈 장비 생성 (기본 무기만 유지)
|
||||
final emptyEquipment = Equipment(
|
||||
items: [
|
||||
EquipmentItem.defaultWeapon(), // 무기 슬롯에 기본 Keyboard
|
||||
EquipmentItem.empty(EquipmentSlot.shield),
|
||||
EquipmentItem.empty(EquipmentSlot.helm),
|
||||
EquipmentItem.empty(EquipmentSlot.hauberk),
|
||||
EquipmentItem.empty(EquipmentSlot.brassairts),
|
||||
EquipmentItem.empty(EquipmentSlot.vambraces),
|
||||
EquipmentItem.empty(EquipmentSlot.gauntlets),
|
||||
EquipmentItem.empty(EquipmentSlot.gambeson),
|
||||
EquipmentItem.empty(EquipmentSlot.cuisses),
|
||||
EquipmentItem.empty(EquipmentSlot.greaves),
|
||||
EquipmentItem.empty(EquipmentSlot.sollerets),
|
||||
],
|
||||
bestIndex: 0,
|
||||
);
|
||||
|
||||
// 사망 정보 생성
|
||||
final deathInfo = DeathInfo(
|
||||
cause: cause,
|
||||
killerName: killerName,
|
||||
lostEquipmentCount: lostCount,
|
||||
goldAtDeath: state.inventory.gold,
|
||||
levelAtDeath: state.traits.level,
|
||||
timestamp: state.skillSystem.elapsedMs,
|
||||
);
|
||||
|
||||
// 전투 상태 초기화
|
||||
final progress = state.progress.copyWith(
|
||||
currentCombat: null,
|
||||
);
|
||||
|
||||
return state.copyWith(
|
||||
equipment: emptyEquipment,
|
||||
progress: progress,
|
||||
deathInfo: deathInfo,
|
||||
);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 부활 처리
|
||||
// ============================================================================
|
||||
|
||||
/// 플레이어 부활 처리
|
||||
///
|
||||
/// 1. HP/MP 전체 회복
|
||||
/// 2. 골드로 구매 가능한 장비 자동 구매
|
||||
/// 3. 사망 상태 해제
|
||||
/// 4. 안전 지역으로 이동 태스크 설정
|
||||
GameState processResurrection(GameState state) {
|
||||
if (!state.isDead) return state;
|
||||
|
||||
// HP/MP 전체 회복
|
||||
var nextState = state.copyWith(
|
||||
stats: state.stats.copyWith(
|
||||
hpCurrent: state.stats.hpMax,
|
||||
mpCurrent: state.stats.mpMax,
|
||||
),
|
||||
);
|
||||
|
||||
// 빈 슬롯에 자동 장비 구매
|
||||
final autoBuyResult = shopService.autoBuyForEmptySlots(
|
||||
playerLevel: nextState.traits.level,
|
||||
currentGold: nextState.inventory.gold,
|
||||
currentEquipment: nextState.equipment,
|
||||
);
|
||||
|
||||
// 결과 적용
|
||||
nextState = nextState.copyWith(
|
||||
equipment: autoBuyResult.updatedEquipment,
|
||||
inventory: nextState.inventory.copyWith(
|
||||
gold: autoBuyResult.remainingGold,
|
||||
),
|
||||
clearDeathInfo: true, // 사망 상태 해제
|
||||
);
|
||||
|
||||
// 스킬 쿨타임 초기화
|
||||
nextState = nextState.copyWith(
|
||||
skillSystem: SkillSystemState.empty().copyWith(
|
||||
elapsedMs: nextState.skillSystem.elapsedMs,
|
||||
),
|
||||
);
|
||||
|
||||
return nextState;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 유틸리티
|
||||
// ============================================================================
|
||||
|
||||
/// 부활 비용 계산 (향후 확장용)
|
||||
///
|
||||
/// 현재는 무료 부활, 향후 비용 도입 가능
|
||||
int calculateResurrectionCost(int playerLevel) {
|
||||
// 기본: 무료
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// 부활 가능 여부 확인
|
||||
bool canResurrect(GameState state) {
|
||||
if (!state.isDead) return false;
|
||||
|
||||
final cost = calculateResurrectionCost(state.traits.level);
|
||||
return state.inventory.gold >= cost;
|
||||
}
|
||||
|
||||
/// 장비 보존 아이템 적용 (향후 확장용)
|
||||
///
|
||||
/// [protectedSlots] 보존할 슬롯 인덱스 목록
|
||||
GameState processDeathWithProtection({
|
||||
required GameState state,
|
||||
required String killerName,
|
||||
required DeathCause cause,
|
||||
required List<int> protectedSlots,
|
||||
}) {
|
||||
// 보존할 아이템 추출
|
||||
final protectedItems = <int, EquipmentItem>{};
|
||||
for (final slotIndex in protectedSlots) {
|
||||
if (slotIndex >= 0 && slotIndex < Equipment.slotCount) {
|
||||
final item = state.equipment.getItemByIndex(slotIndex);
|
||||
if (item.isNotEmpty) {
|
||||
protectedItems[slotIndex] = item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 기본 사망 처리
|
||||
var nextState = processDeath(
|
||||
state: state,
|
||||
killerName: killerName,
|
||||
cause: cause,
|
||||
);
|
||||
|
||||
// 보존된 아이템 복원
|
||||
var equipment = nextState.equipment;
|
||||
for (final entry in protectedItems.entries) {
|
||||
equipment = equipment.setItemByIndex(entry.key, entry.value);
|
||||
}
|
||||
|
||||
// 상실 개수 재계산
|
||||
final actualLostCount =
|
||||
state.equipment.equippedItems.length - protectedItems.length;
|
||||
|
||||
return nextState.copyWith(
|
||||
equipment: equipment,
|
||||
deathInfo: nextState.deathInfo?.copyWith(
|
||||
lostEquipmentCount: actualLostCount,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user