feat(engine): 엔진 서비스 개선 및 테스트 캐릭터 서비스 추가
- ProgressService 로직 개선 - RewardService 확장 - CombatCalculator, ItemService 정리 - TestCharacterService 추가
This commit is contained in:
@@ -6,6 +6,7 @@ import 'package:asciineverdie/src/core/engine/combat_calculator.dart';
|
||||
import 'package:asciineverdie/src/core/engine/game_mutations.dart';
|
||||
import 'package:asciineverdie/src/core/engine/potion_service.dart';
|
||||
import 'package:asciineverdie/src/core/engine/reward_service.dart';
|
||||
import 'package:asciineverdie/src/core/engine/shop_service.dart';
|
||||
import 'package:asciineverdie/src/core/engine/skill_service.dart';
|
||||
import 'package:asciineverdie/src/core/model/combat_event.dart';
|
||||
import 'package:asciineverdie/src/core/model/combat_state.dart';
|
||||
@@ -13,6 +14,7 @@ import 'package:asciineverdie/src/core/model/combat_stats.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';
|
||||
import 'package:asciineverdie/src/core/model/monster_combat_stats.dart';
|
||||
import 'package:asciineverdie/src/core/model/monster_grade.dart';
|
||||
import 'package:asciineverdie/src/core/model/potion.dart';
|
||||
@@ -514,12 +516,15 @@ class ProgressService {
|
||||
return (progress: progress, queue: queue);
|
||||
}
|
||||
|
||||
// 2. kill 태스크가 아니었고 heading도 아니면 heading 또는 buying 태스크 실행
|
||||
// (원본 670-677줄)
|
||||
if (oldTaskType != TaskType.kill && oldTaskType != TaskType.neutral) {
|
||||
// Gold가 충분하면 장비 구매 (원본 671-673줄)
|
||||
// 2. kill/heading/buying 태스크가 아니었으면 heading 또는 buying 태스크 실행
|
||||
// (원본 670-677줄) - buying 완료 후 무한 루프 방지
|
||||
if (oldTaskType != TaskType.kill &&
|
||||
oldTaskType != TaskType.neutral &&
|
||||
oldTaskType != TaskType.buying) {
|
||||
// Gold가 충분하면 장비 구매 (Common 장비 가격 기준)
|
||||
// 실제 구매 가격과 동일한 공식 사용: level * 50
|
||||
final gold = _getGold(state);
|
||||
final equipPrice = _equipPrice(state.traits.level);
|
||||
final equipPrice = state.traits.level * 50; // Common 장비 1개 가격
|
||||
if (gold > equipPrice) {
|
||||
final taskResult = pq_logic.startTask(
|
||||
progress,
|
||||
@@ -966,8 +971,12 @@ class ProgressService {
|
||||
|
||||
final nextLevel = state.traits.level + 1;
|
||||
final rng = state.rng;
|
||||
final hpGain = state.stats.con ~/ 3 + 1 + rng.nextInt(4);
|
||||
final mpGain = state.stats.intelligence ~/ 3 + 1 + rng.nextInt(4);
|
||||
|
||||
// HP/MP 증가량 (PlayerScaling 기반 + 랜덤 변동)
|
||||
// 기존: CON/3 + 1 + random(0-3) → ~6-9 HP/레벨 (너무 낮음)
|
||||
// 신규: 18 + CON/5 + random(0-4) → ~20-25 HP/레벨 (생존율 개선)
|
||||
final hpGain = 18 + state.stats.con ~/ 5 + rng.nextInt(5);
|
||||
final mpGain = 6 + state.stats.intelligence ~/ 5 + rng.nextInt(3);
|
||||
|
||||
var nextState = state.copyWith(
|
||||
traits: state.traits.copyWith(level: nextLevel),
|
||||
@@ -1078,29 +1087,48 @@ class ProgressService {
|
||||
return state.inventory.gold;
|
||||
}
|
||||
|
||||
/// 장비 가격 계산 (원본 Main.pas:612-616)
|
||||
/// Result := 5 * Level^2 + 10 * Level + 20
|
||||
int _equipPrice(int level) {
|
||||
return 5 * level * level + 10 * level + 20;
|
||||
}
|
||||
|
||||
/// 장비 구매 완료 처리 (원본 Main.pas:631-634)
|
||||
/// 장비 구매 완료 처리 (개선된 로직)
|
||||
///
|
||||
/// 1순위: 빈 슬롯에 Common 장비 최대한 채우기
|
||||
/// 2순위: 골드 남으면 물약 구매
|
||||
GameState _completeBuying(GameState state) {
|
||||
var nextState = state;
|
||||
final level = state.traits.level;
|
||||
final price = _equipPrice(level);
|
||||
final shopService = ShopService(rng: nextState.rng);
|
||||
|
||||
// Gold 차감 (inventory.gold 필드 사용)
|
||||
final newGold = math.max(0, state.inventory.gold - price);
|
||||
var nextState = state.copyWith(
|
||||
inventory: state.inventory.copyWith(gold: newGold),
|
||||
);
|
||||
// 1. 빈 슬롯 목록 수집
|
||||
final emptySlots = <int>[];
|
||||
for (var i = 0; i < Equipment.slotCount; i++) {
|
||||
if (nextState.equipment.getItemByIndex(i).isEmpty) {
|
||||
emptySlots.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
// 장비 획득 (WinEquip)
|
||||
// 원본 Main.pas:797 - posn := Random(Equips.Items.Count); (11개 슬롯)
|
||||
final slotIndex = nextState.rng.nextInt(Equipment.slotCount);
|
||||
nextState = mutations.winEquipByIndex(nextState, level, slotIndex);
|
||||
// 2. 골드가 허용하는 한 빈 슬롯에 Common 장비 구매
|
||||
for (final slotIndex in emptySlots) {
|
||||
final slot = EquipmentSlot.values[slotIndex];
|
||||
final item = shopService.generateShopItem(
|
||||
playerLevel: level,
|
||||
slot: slot,
|
||||
targetRarity: ItemRarity.common,
|
||||
);
|
||||
final price = shopService.calculateBuyPrice(item);
|
||||
|
||||
// 물약 자동 구매 (남은 골드의 20% 사용)
|
||||
if (nextState.inventory.gold >= price) {
|
||||
nextState = nextState.copyWith(
|
||||
inventory: nextState.inventory.copyWith(
|
||||
gold: nextState.inventory.gold - price,
|
||||
),
|
||||
equipment: nextState.equipment
|
||||
.setItemByIndex(slotIndex, item)
|
||||
.copyWith(bestIndex: slotIndex),
|
||||
);
|
||||
} else {
|
||||
break; // 골드 부족 시 중단
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 물약 자동 구매 (남은 골드의 20% 사용)
|
||||
final potionService = const PotionService();
|
||||
final purchaseResult = potionService.autoPurchasePotions(
|
||||
playerLevel: level,
|
||||
@@ -1644,30 +1672,35 @@ class ProgressService {
|
||||
required String killerName,
|
||||
required DeathCause cause,
|
||||
}) {
|
||||
// 상실할 장비 개수 계산
|
||||
final lostCount = state.equipment.equippedItems.length;
|
||||
|
||||
// 사망 직전 전투 이벤트 저장 (최대 10개)
|
||||
final lastCombatEvents =
|
||||
state.progress.currentCombat?.recentEvents ?? const [];
|
||||
|
||||
// 빈 장비 생성 (기본 무기만 유지)
|
||||
final emptyEquipment = Equipment(
|
||||
items: [
|
||||
EquipmentItem.defaultWeapon(),
|
||||
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,
|
||||
);
|
||||
// 무기(슬롯 0)를 제외한 장착된 장비 중 1개를 제물로 삭제
|
||||
// 장착된 비무기 슬롯 인덱스 수집 (슬롯 1~10 중 장비가 있는 것)
|
||||
final equippedNonWeaponSlots = <int>[];
|
||||
for (var i = 1; i < Equipment.slotCount; i++) {
|
||||
if (state.equipment.getItemByIndex(i).isNotEmpty) {
|
||||
equippedNonWeaponSlots.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
// 제물로 바칠 장비 선택 및 삭제
|
||||
var newEquipment = state.equipment;
|
||||
final lostCount = equippedNonWeaponSlots.isNotEmpty ? 1 : 0;
|
||||
|
||||
if (equippedNonWeaponSlots.isNotEmpty) {
|
||||
// 랜덤하게 1개 슬롯 선택
|
||||
final sacrificeIndex =
|
||||
equippedNonWeaponSlots[state.rng.nextInt(equippedNonWeaponSlots.length)];
|
||||
final slot = EquipmentSlot.values[sacrificeIndex];
|
||||
|
||||
// 해당 슬롯을 빈 장비로 교체
|
||||
newEquipment = newEquipment.setItemByIndex(
|
||||
sacrificeIndex,
|
||||
EquipmentItem.empty(slot),
|
||||
);
|
||||
}
|
||||
|
||||
// 사망 정보 생성 (전투 로그 포함)
|
||||
final deathInfo = DeathInfo(
|
||||
@@ -1689,7 +1722,7 @@ class ProgressService {
|
||||
);
|
||||
|
||||
return state.copyWith(
|
||||
equipment: emptyEquipment,
|
||||
equipment: newEquipment,
|
||||
progress: progress,
|
||||
deathInfo: deathInfo,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user