import 'dart:math' as math; import 'package:asciineverdie/src/core/model/equipment_slot.dart'; import 'package:asciineverdie/src/core/model/pq_config.dart'; import 'package:asciineverdie/src/core/util/deterministic_random.dart'; import 'package:asciineverdie/src/core/util/pq_random.dart'; /// 아이템/장비 생성 관련 함수들 /// 원본 Main.pas의 아이템 생성 로직을 포팅 /// 장비 생성 결과 (구조화된 데이터로 l10n 지원) class EquipResult { const EquipResult({ required this.baseName, this.modifiers = const [], this.plusValue = 0, }); /// 기본 장비 이름 (예: "VPN Cloak") final String baseName; /// 수식어 목록 (예: ["Holey", "Deprecated"]) final List modifiers; /// +/- 수치 (예: -1, +2) final int plusValue; /// 영문 전체 이름 생성 (기존 방식) String get displayName { var name = baseName; for (final mod in modifiers) { name = '$mod $name'; } if (plusValue != 0) { name = '${plusValue > 0 ? '+' : ''}$plusValue $name'; } return name; } } /// 아이템 생성 결과 (구조화된 데이터로 l10n 지원) class ItemResult { const ItemResult({this.attrib, this.special, this.itemOf, this.boringItem}); /// 아이템 속성 (예: "Golden") final String? attrib; /// 특수 아이템 (예: "Iterator") final String? special; /// "~의" 접미사 (예: "Monitoring") final String? itemOf; /// 단순 아이템 (보링 아이템용) final String? boringItem; /// 영문 전체 이름 생성 (기존 방식) String get displayName { if (boringItem != null) return boringItem!; if (attrib != null && special != null && itemOf != null) { return '$attrib $special of $itemOf'; } if (attrib != null && special != null) { return '$attrib $special'; } return ''; } } // ============================================================================= // 아이템 생성 함수들 // ============================================================================= /// 단순 아이템 (Boring Item) String boringItem(PqConfig config, DeterministicRandom rng) { return pick(config.boringItems, rng); } /// 흥미로운 아이템 (Interesting Item) String interestingItem(PqConfig config, DeterministicRandom rng) { final attr = pick(config.itemAttrib, rng); final special = pick(config.specials, rng); return '$attr $special'; } /// 특수 아이템 (Special Item) String specialItem(PqConfig config, DeterministicRandom rng) { return '${interestingItem(config, rng)} of ${pick(config.itemOfs, rng)}'; } /// 구조화된 단순 아이템 결과 반환 (l10n 지원) ItemResult boringItemStructured(PqConfig config, DeterministicRandom rng) { return ItemResult(boringItem: pick(config.boringItems, rng)); } /// 구조화된 흥미로운 아이템 결과 반환 (l10n 지원) ItemResult interestingItemStructured(PqConfig config, DeterministicRandom rng) { return ItemResult( attrib: pick(config.itemAttrib, rng), special: pick(config.specials, rng), ); } /// 구조화된 특수 아이템 결과 반환 (l10n 지원) ItemResult specialItemStructured(PqConfig config, DeterministicRandom rng) { return ItemResult( attrib: pick(config.itemAttrib, rng), special: pick(config.specials, rng), itemOf: pick(config.itemOfs, rng), ); } /// 구조화된 승리 아이템 결과 반환 (l10n 지원) ItemResult winItemStructured( PqConfig config, DeterministicRandom rng, int inventoryCount, ) { final threshold = math.max(250, rng.nextInt(999)); if (inventoryCount > threshold) { return const ItemResult(); // 빈 결과 } return specialItemStructured(config, rng); } /// 승리 아이템 (문자열 반환) String winItem(PqConfig config, DeterministicRandom rng, int inventoryCount) { final threshold = math.max(250, rng.nextInt(999)); if (inventoryCount > threshold) return ''; return specialItem(config, rng); } // ============================================================================= // 장비 선택 함수들 // ============================================================================= /// 무기 선택 String pickWeapon(PqConfig config, DeterministicRandom rng, int level) { return _lPick(config.weapons, rng, level); } /// 방패 선택 String pickShield(PqConfig config, DeterministicRandom rng, int level) { return _lPick(config.shields, rng, level); } /// 방어구 선택 String pickArmor(PqConfig config, DeterministicRandom rng, int level) { return _lPick(config.armors, rng, level); } /// 주문 선택 String pickSpell(PqConfig config, DeterministicRandom rng, int goalLevel) { return _lPick(config.spells, rng, goalLevel); } /// 원본 Main.pas:776-789 LPick: 6회 시도하여 목표 레벨에 가장 가까운 아이템 선택 String _lPick(List items, DeterministicRandom rng, int goal) { if (items.isEmpty) return ''; var result = pick(items, rng); var bestLevel = _parseLevel(result); for (var i = 0; i < 5; i++) { final candidate = pick(items, rng); final candLevel = _parseLevel(candidate); if ((goal - candLevel).abs() < (goal - bestLevel).abs()) { result = candidate; bestLevel = candLevel; } } return result; } /// 아이템 문자열에서 레벨 파싱 int _parseLevel(String entry) { final parts = entry.split('|'); if (parts.length < 2) return 0; return int.tryParse(parts[1].replaceAll('+', '')) ?? 0; } // ============================================================================= // 장비 생성 함수들 // ============================================================================= /// 수식어 추가 String addModifier( DeterministicRandom rng, String baseName, List modifiers, int plus, ) { var name = baseName; var remaining = plus; var count = 0; while (count < 2 && remaining != 0) { final modifier = pick(modifiers, rng); final parts = modifier.split('|'); if (parts.isEmpty) break; final label = parts[0]; final qual = parts.length > 1 ? int.tryParse(parts[1]) ?? 0 : 0; if (name.contains(label)) break; // avoid repeats if (remaining.abs() < qual.abs()) break; name = '$label $name'; remaining -= qual; count++; } if (remaining != 0) { name = '${remaining > 0 ? '+' : ''}$remaining $name'; } return name; } /// 장비 생성 (원본 Main.pas:791-830 WinEquip) /// [slotIndex]: 0=Weapon, 1=Shield, 2-10=Armor 계열 String winEquip( PqConfig config, DeterministicRandom rng, int level, int slotIndex, ) { final bool isWeapon = slotIndex == 0; final List items; if (slotIndex == 0) { items = config.weapons; } else if (slotIndex == 1) { items = config.shields; } else { items = config.armors; } final better = isWeapon ? config.offenseAttrib : config.defenseAttrib; final worse = isWeapon ? config.offenseBad : config.defenseBad; final base = _lPick(items, rng, level); final parts = base.split('|'); final baseName = parts[0]; final qual = parts.length > 1 ? int.tryParse(parts[1].replaceAll('+', '')) ?? 0 : 0; final plus = level - qual; final modifierList = plus >= 0 ? better : worse; return addModifier(rng, baseName, modifierList, plus); } /// EquipmentSlot enum을 사용하는 편의 함수 String winEquipBySlot( PqConfig config, DeterministicRandom rng, int level, EquipmentSlot slot, ) { return winEquip(config, rng, level, slot.index); } /// 구조화된 장비 생성 결과 반환 (l10n 지원) EquipResult winEquipStructured( PqConfig config, DeterministicRandom rng, int level, int slotIndex, ) { final bool isWeapon = slotIndex == 0; final List items; if (slotIndex == 0) { items = config.weapons; } else if (slotIndex == 1) { items = config.shields; } else { items = config.armors; } final better = isWeapon ? config.offenseAttrib : config.defenseAttrib; final worse = isWeapon ? config.offenseBad : config.defenseBad; final base = _lPick(items, rng, level); final parts = base.split('|'); final baseName = parts[0]; final qual = parts.length > 1 ? int.tryParse(parts[1].replaceAll('+', '')) ?? 0 : 0; final plus = level - qual; final modifierList = plus >= 0 ? better : worse; return _addModifierStructured(rng, baseName, modifierList, plus); } /// 구조화된 장비 결과 반환 (내부 함수) EquipResult _addModifierStructured( DeterministicRandom rng, String baseName, List modifiers, int plus, ) { final collectedModifiers = []; var remaining = plus; var count = 0; while (count < 2 && remaining != 0) { final modifier = pick(modifiers, rng); final parts = modifier.split('|'); if (parts.isEmpty) break; final label = parts[0]; final qual = parts.length > 1 ? int.tryParse(parts[1]) ?? 0 : 0; if (collectedModifiers.contains(label)) break; // avoid repeats if (remaining.abs() < qual.abs()) break; collectedModifiers.add(label); remaining -= qual; count++; } return EquipResult( baseName: baseName, modifiers: collectedModifiers, plusValue: remaining, ); } /// EquipmentSlot enum을 사용하는 구조화된 버전 EquipResult winEquipBySlotStructured( PqConfig config, DeterministicRandom rng, int level, EquipmentSlot slot, ) { return winEquipStructured(config, rng, level, slot.index); }