style(lint): flutter analyze 경고 91건 → 0건 전체 정리
- analysis_options.yaml: freezed/g.dart 생성 파일 분석 제외
- game_text_l10n.dart: if문 중괄호 추가, 불필요한 ${} 제거
- iap_service.dart: 불필요한 dart:typed_data import 제거
- pq_logic.dart, combat_text_frames.dart: dangling library doc → library; 추가
- save_picker_dialog.dart: __ → _ (unnecessary_underscores)
- desktop_equipment_panel.dart: 불필요한 import 제거
- test 파일: _localVar → localVar 네이밍, ignore_for_file 추가
This commit is contained in:
@@ -14,6 +14,9 @@ analyzer:
|
|||||||
strict-casts: true
|
strict-casts: true
|
||||||
strict-inference: true
|
strict-inference: true
|
||||||
strict-raw-types: true
|
strict-raw-types: true
|
||||||
|
exclude:
|
||||||
|
- "**/*.freezed.dart"
|
||||||
|
- "**/*.g.dart"
|
||||||
|
|
||||||
linter:
|
linter:
|
||||||
# Keep the rule set lean; we will tighten as the engine port stabilizes.
|
# Keep the rule set lean; we will tighten as the engine port stabilizes.
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ String get speedBoostTitle => _l('Speed Boost', '속도 부스트', 'スピー
|
|||||||
String get speedBoostActivate =>
|
String get speedBoostActivate =>
|
||||||
_l('Activate 10x Speed', '10배속 활성화', '10倍速を有効化');
|
_l('Activate 10x Speed', '10배속 활성화', '10倍速を有効化');
|
||||||
String speedBoostRemaining(int seconds) =>
|
String speedBoostRemaining(int seconds) =>
|
||||||
_l('${seconds}s remaining', '${seconds}초 남음', '残り${seconds}秒');
|
_l('${seconds}s remaining', '$seconds초 남음', '残り$seconds秒');
|
||||||
String get speedBoostActive => _l('BOOST ACTIVE', '부스트 활성화', 'ブースト中');
|
String get speedBoostActive => _l('BOOST ACTIVE', '부스트 활성화', 'ブースト中');
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -650,8 +650,9 @@ String translateImpressiveTitle(String englishName) {
|
|||||||
/// 특수 아이템 이름 번역
|
/// 특수 아이템 이름 번역
|
||||||
String translateSpecial(String englishName) {
|
String translateSpecial(String englishName) {
|
||||||
if (isKoreanLocale) return specialTranslationsKo[englishName] ?? englishName;
|
if (isKoreanLocale) return specialTranslationsKo[englishName] ?? englishName;
|
||||||
if (isJapaneseLocale)
|
if (isJapaneseLocale) {
|
||||||
return specialTranslationsJa[englishName] ?? englishName;
|
return specialTranslationsJa[englishName] ?? englishName;
|
||||||
|
}
|
||||||
return englishName;
|
return englishName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -828,54 +829,65 @@ String translateItemNameL10n(String itemString) {
|
|||||||
|
|
||||||
/// Act 제목 번역
|
/// Act 제목 번역
|
||||||
String translateActTitle(String englishTitle) {
|
String translateActTitle(String englishTitle) {
|
||||||
if (isKoreanLocale)
|
if (isKoreanLocale) {
|
||||||
return actTitleTranslationsKo[englishTitle] ?? englishTitle;
|
return actTitleTranslationsKo[englishTitle] ?? englishTitle;
|
||||||
if (isJapaneseLocale)
|
}
|
||||||
|
if (isJapaneseLocale) {
|
||||||
return actTitleTranslationsJa[englishTitle] ?? englishTitle;
|
return actTitleTranslationsJa[englishTitle] ?? englishTitle;
|
||||||
|
}
|
||||||
return englishTitle;
|
return englishTitle;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Act 보스 이름 번역
|
/// Act 보스 이름 번역
|
||||||
String translateActBoss(String englishBoss) {
|
String translateActBoss(String englishBoss) {
|
||||||
if (isKoreanLocale) return actBossTranslationsKo[englishBoss] ?? englishBoss;
|
if (isKoreanLocale) return actBossTranslationsKo[englishBoss] ?? englishBoss;
|
||||||
if (isJapaneseLocale)
|
if (isJapaneseLocale) {
|
||||||
return actBossTranslationsJa[englishBoss] ?? englishBoss;
|
return actBossTranslationsJa[englishBoss] ?? englishBoss;
|
||||||
|
}
|
||||||
return englishBoss;
|
return englishBoss;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Act 퀘스트 번역
|
/// Act 퀘스트 번역
|
||||||
String translateActQuest(String englishQuest) {
|
String translateActQuest(String englishQuest) {
|
||||||
if (isKoreanLocale)
|
if (isKoreanLocale) {
|
||||||
return actQuestTranslationsKo[englishQuest] ?? englishQuest;
|
return actQuestTranslationsKo[englishQuest] ?? englishQuest;
|
||||||
if (isJapaneseLocale)
|
}
|
||||||
|
if (isJapaneseLocale) {
|
||||||
return actQuestTranslationsJa[englishQuest] ?? englishQuest;
|
return actQuestTranslationsJa[englishQuest] ?? englishQuest;
|
||||||
|
}
|
||||||
return englishQuest;
|
return englishQuest;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 시네마틱 텍스트 번역
|
/// 시네마틱 텍스트 번역
|
||||||
String translateCinematic(String englishText) {
|
String translateCinematic(String englishText) {
|
||||||
if (isKoreanLocale)
|
if (isKoreanLocale) {
|
||||||
return cinematicTranslationsKo[englishText] ?? englishText;
|
return cinematicTranslationsKo[englishText] ?? englishText;
|
||||||
if (isJapaneseLocale)
|
}
|
||||||
|
if (isJapaneseLocale) {
|
||||||
return cinematicTranslationsJa[englishText] ?? englishText;
|
return cinematicTranslationsJa[englishText] ?? englishText;
|
||||||
|
}
|
||||||
return englishText;
|
return englishText;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 지역 이름 번역
|
/// 지역 이름 번역
|
||||||
String translateLocation(String englishLocation) {
|
String translateLocation(String englishLocation) {
|
||||||
if (isKoreanLocale)
|
if (isKoreanLocale) {
|
||||||
return locationTranslationsKo[englishLocation] ?? englishLocation;
|
return locationTranslationsKo[englishLocation] ?? englishLocation;
|
||||||
if (isJapaneseLocale)
|
}
|
||||||
|
if (isJapaneseLocale) {
|
||||||
return locationTranslationsJa[englishLocation] ?? englishLocation;
|
return locationTranslationsJa[englishLocation] ?? englishLocation;
|
||||||
|
}
|
||||||
return englishLocation;
|
return englishLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 세력/조직 이름 번역
|
/// 세력/조직 이름 번역
|
||||||
String translateFaction(String englishFaction) {
|
String translateFaction(String englishFaction) {
|
||||||
if (isKoreanLocale)
|
if (isKoreanLocale) {
|
||||||
return factionTranslationsKo[englishFaction] ?? englishFaction;
|
return factionTranslationsKo[englishFaction] ?? englishFaction;
|
||||||
if (isJapaneseLocale)
|
}
|
||||||
|
if (isJapaneseLocale) {
|
||||||
return factionTranslationsJa[englishFaction] ?? englishFaction;
|
return factionTranslationsJa[englishFaction] ?? englishFaction;
|
||||||
|
}
|
||||||
return englishFaction;
|
return englishFaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1233,7 +1245,7 @@ String get notifyQuestComplete => _l('QUEST COMPLETE!', '퀘스트 완료!', '
|
|||||||
String get notifyPrologueComplete =>
|
String get notifyPrologueComplete =>
|
||||||
_l('PROLOGUE COMPLETE!', '프롤로그 완료!', 'プロローグ完了!');
|
_l('PROLOGUE COMPLETE!', '프롤로그 완료!', 'プロローグ完了!');
|
||||||
String notifyActComplete(int actNumber) =>
|
String notifyActComplete(int actNumber) =>
|
||||||
_l('ACT $actNumber COMPLETE!', '${actNumber}막 완료!', '第${actNumber}幕完了!');
|
_l('ACT $actNumber COMPLETE!', '$actNumber막 완료!', '第$actNumber幕完了!');
|
||||||
String get notifyNewSpell => _l('NEW SPELL!', '새 주문!', '新しい呪文!');
|
String get notifyNewSpell => _l('NEW SPELL!', '새 주문!', '新しい呪文!');
|
||||||
String get notifyNewEquipment => _l('NEW EQUIPMENT!', '새 장비!', '新しい装備!');
|
String get notifyNewEquipment => _l('NEW EQUIPMENT!', '새 장비!', '新しい装備!');
|
||||||
String get notifyBossDefeated => _l('BOSS DEFEATED!', '보스 처치!', 'ボス撃破!');
|
String get notifyBossDefeated => _l('BOSS DEFEATED!', '보스 처치!', 'ボス撃破!');
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
import 'package:in_app_purchase/in_app_purchase.dart';
|
import 'package:in_app_purchase/in_app_purchase.dart';
|
||||||
@@ -190,10 +188,7 @@ class IAPService implements IIAPService {
|
|||||||
|
|
||||||
/// 구매 상태 저장 (보안 저장소 사용)
|
/// 구매 상태 저장 (보안 저장소 사용)
|
||||||
Future<void> _savePurchaseState(bool purchased) async {
|
Future<void> _savePurchaseState(bool purchased) async {
|
||||||
await _secureStorage.write(
|
await _secureStorage.write(key: _purchaseKey, value: purchased.toString());
|
||||||
key: _purchaseKey,
|
|
||||||
value: purchased.toString(),
|
|
||||||
);
|
|
||||||
_adRemovalPurchased = purchased;
|
_adRemovalPurchased = purchased;
|
||||||
debugPrint('[IAPService] Saved purchase state: $purchased');
|
debugPrint('[IAPService] Saved purchase state: $purchased');
|
||||||
}
|
}
|
||||||
@@ -451,10 +446,7 @@ class IAPService implements IIAPService {
|
|||||||
pc.PublicKeyParameter<pc.RSAPublicKey>(publicKey),
|
pc.PublicKeyParameter<pc.RSAPublicKey>(publicKey),
|
||||||
);
|
);
|
||||||
|
|
||||||
return signer.verifySignature(
|
return signer.verifySignature(dataBytes, pc.RSASignature(signatureBytes));
|
||||||
dataBytes,
|
|
||||||
pc.RSASignature(signatureBytes),
|
|
||||||
);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('[IAPService] RSA verification error: $e');
|
debugPrint('[IAPService] RSA verification error: $e');
|
||||||
return false;
|
return false;
|
||||||
@@ -477,10 +469,8 @@ class IAPService implements IIAPService {
|
|||||||
final rsaParser = ASN1Parser(publicKeyBytes);
|
final rsaParser = ASN1Parser(publicKeyBytes);
|
||||||
final rsaSequence = rsaParser.nextObject() as ASN1Sequence;
|
final rsaSequence = rsaParser.nextObject() as ASN1Sequence;
|
||||||
|
|
||||||
final modulus =
|
final modulus = (rsaSequence.elements![0] as ASN1Integer).integer!;
|
||||||
(rsaSequence.elements![0] as ASN1Integer).integer!;
|
final exponent = (rsaSequence.elements![1] as ASN1Integer).integer!;
|
||||||
final exponent =
|
|
||||||
(rsaSequence.elements![1] as ASN1Integer).integer!;
|
|
||||||
|
|
||||||
return pc.RSAPublicKey(modulus, exponent);
|
return pc.RSAPublicKey(modulus, exponent);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
///
|
///
|
||||||
/// 유틸리티 함수 모음.
|
/// 유틸리티 함수 모음.
|
||||||
/// 이 파일은 분할된 모듈들을 re-export하여 기존 코드 호환성 유지.
|
/// 이 파일은 분할된 모듈들을 re-export하여 기존 코드 호환성 유지.
|
||||||
|
library;
|
||||||
|
|
||||||
// 랜덤/확률 함수
|
// 랜덤/확률 함수
|
||||||
export 'package:asciineverdie/src/core/util/pq_random.dart';
|
export 'package:asciineverdie/src/core/util/pq_random.dart';
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class SavePickerDialog extends StatelessWidget {
|
|||||||
child: ListView.separated(
|
child: ListView.separated(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: saves.length,
|
itemCount: saves.length,
|
||||||
separatorBuilder: (_, __) => const Divider(height: 1),
|
separatorBuilder: (_, _) => const Divider(height: 1),
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final save = saves[index];
|
final save = saves[index];
|
||||||
return _SaveListTile(
|
return _SaveListTile(
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import 'package:asciineverdie/data/game_text_l10n.dart' as game_l10n;
|
|||||||
import 'package:asciineverdie/l10n/app_localizations.dart';
|
import 'package:asciineverdie/l10n/app_localizations.dart';
|
||||||
import 'package:asciineverdie/src/shared/l10n/game_data_l10n.dart';
|
import 'package:asciineverdie/src/shared/l10n/game_data_l10n.dart';
|
||||||
import 'package:asciineverdie/src/core/model/game_state.dart';
|
import 'package:asciineverdie/src/core/model/game_state.dart';
|
||||||
import 'package:asciineverdie/src/core/model/inventory.dart';
|
|
||||||
import 'package:asciineverdie/src/features/game/widgets/combat_log.dart';
|
import 'package:asciineverdie/src/features/game/widgets/combat_log.dart';
|
||||||
import 'package:asciineverdie/src/features/game/widgets/desktop_panel_widgets.dart';
|
import 'package:asciineverdie/src/features/game/widgets/desktop_panel_widgets.dart';
|
||||||
import 'package:asciineverdie/src/features/game/widgets/equipment_stats_panel.dart';
|
import 'package:asciineverdie/src/features/game/widgets/equipment_stats_panel.dart';
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
///
|
///
|
||||||
/// CanvasBattleComposer에서 분리된 전투 텍스트 프레임 상수.
|
/// CanvasBattleComposer에서 분리된 전투 텍스트 프레임 상수.
|
||||||
/// 크리티컬, 회피, 미스, 디버프, DOT, 블록, 패리 텍스트 프레임.
|
/// 크리티컬, 회피, 미스, 디버프, DOT, 블록, 패리 텍스트 프레임.
|
||||||
|
library;
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// 몬스터 공격 이펙트 (← 방향, Phase 8) - 5줄
|
// 몬스터 공격 이펙트 (← 방향, Phase 8) - 5줄
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'package:asciineverdie/src/core/engine/death_handler.dart';
|
import 'package:asciineverdie/src/core/engine/death_handler.dart';
|
||||||
import 'package:asciineverdie/src/core/model/equipment_container.dart';
|
|
||||||
import 'package:asciineverdie/src/core/model/equipment_item.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/equipment_slot.dart';
|
||||||
import 'package:asciineverdie/src/core/model/game_state.dart';
|
import 'package:asciineverdie/src/core/model/game_state.dart';
|
||||||
@@ -13,7 +12,7 @@ void main() {
|
|||||||
const handler = DeathHandler();
|
const handler = DeathHandler();
|
||||||
|
|
||||||
/// 테스트용 장비 생성 헬퍼
|
/// 테스트용 장비 생성 헬퍼
|
||||||
EquipmentItem _makeItem(String name, EquipmentSlot slot) {
|
EquipmentItem makeItem(String name, EquipmentSlot slot) {
|
||||||
return EquipmentItem(
|
return EquipmentItem(
|
||||||
name: name,
|
name: name,
|
||||||
slot: slot,
|
slot: slot,
|
||||||
@@ -25,20 +24,20 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 모든 슬롯에 장비가 장착된 Equipment 생성
|
/// 모든 슬롯에 장비가 장착된 Equipment 생성
|
||||||
Equipment _fullyEquipped() {
|
Equipment fullyEquipped() {
|
||||||
return Equipment(
|
return Equipment(
|
||||||
items: [
|
items: [
|
||||||
EquipmentItem.defaultWeapon(), // 0: 무기(weapon)
|
EquipmentItem.defaultWeapon(), // 0: 무기(weapon)
|
||||||
_makeItem('Iron Shield', EquipmentSlot.shield),
|
makeItem('Iron Shield', EquipmentSlot.shield),
|
||||||
_makeItem('Iron Helm', EquipmentSlot.helm),
|
makeItem('Iron Helm', EquipmentSlot.helm),
|
||||||
_makeItem('Iron Hauberk', EquipmentSlot.hauberk),
|
makeItem('Iron Hauberk', EquipmentSlot.hauberk),
|
||||||
_makeItem('Iron Brassairts', EquipmentSlot.brassairts),
|
makeItem('Iron Brassairts', EquipmentSlot.brassairts),
|
||||||
_makeItem('Iron Vambraces', EquipmentSlot.vambraces),
|
makeItem('Iron Vambraces', EquipmentSlot.vambraces),
|
||||||
_makeItem('Iron Gauntlets', EquipmentSlot.gauntlets),
|
makeItem('Iron Gauntlets', EquipmentSlot.gauntlets),
|
||||||
_makeItem('Iron Gambeson', EquipmentSlot.gambeson),
|
makeItem('Iron Gambeson', EquipmentSlot.gambeson),
|
||||||
_makeItem('Iron Cuisses', EquipmentSlot.cuisses),
|
makeItem('Iron Cuisses', EquipmentSlot.cuisses),
|
||||||
_makeItem('Iron Greaves', EquipmentSlot.greaves),
|
makeItem('Iron Greaves', EquipmentSlot.greaves),
|
||||||
_makeItem('Iron Sollerets', EquipmentSlot.sollerets),
|
makeItem('Iron Sollerets', EquipmentSlot.sollerets),
|
||||||
],
|
],
|
||||||
bestIndex: 0,
|
bestIndex: 0,
|
||||||
);
|
);
|
||||||
@@ -50,7 +49,7 @@ void main() {
|
|||||||
/// [level]: 캐릭터 레벨
|
/// [level]: 캐릭터 레벨
|
||||||
/// [isBossFight]: 보스전(boss fight) 여부
|
/// [isBossFight]: 보스전(boss fight) 여부
|
||||||
/// [equipment]: 커스텀 장비
|
/// [equipment]: 커스텀 장비
|
||||||
GameState _createState({
|
GameState createState({
|
||||||
int seed = 42,
|
int seed = 42,
|
||||||
int level = 1,
|
int level = 1,
|
||||||
bool isBossFight = false,
|
bool isBossFight = false,
|
||||||
@@ -59,7 +58,7 @@ void main() {
|
|||||||
final state = GameState(
|
final state = GameState(
|
||||||
rng: DeterministicRandom(seed),
|
rng: DeterministicRandom(seed),
|
||||||
traits: Traits.empty().copyWith(level: level),
|
traits: Traits.empty().copyWith(level: level),
|
||||||
equipment: equipment ?? _fullyEquipped(),
|
equipment: equipment ?? fullyEquipped(),
|
||||||
progress: ProgressState.empty().copyWith(
|
progress: ProgressState.empty().copyWith(
|
||||||
currentCombat: MockFactories.createCombat(),
|
currentCombat: MockFactories.createCombat(),
|
||||||
finalBossState: isBossFight
|
finalBossState: isBossFight
|
||||||
@@ -75,7 +74,7 @@ void main() {
|
|||||||
group('deathInfo 생성', () {
|
group('deathInfo 생성', () {
|
||||||
test('사망 시 deathInfo가 올바르게 생성된다', () {
|
test('사망 시 deathInfo가 올바르게 생성된다', () {
|
||||||
// 준비(arrange): Lv10 캐릭터 - 100% 장비 손실 확률
|
// 준비(arrange): Lv10 캐릭터 - 100% 장비 손실 확률
|
||||||
final state = _createState(level: 10);
|
final state = createState(level: 10);
|
||||||
|
|
||||||
// 실행(act)
|
// 실행(act)
|
||||||
final result = handler.processPlayerDeath(
|
final result = handler.processPlayerDeath(
|
||||||
@@ -94,7 +93,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('selfDamage 원인으로 사망 시 cause가 올바르다', () {
|
test('selfDamage 원인으로 사망 시 cause가 올바르다', () {
|
||||||
final state = _createState(level: 1);
|
final state = createState(level: 1);
|
||||||
|
|
||||||
final result = handler.processPlayerDeath(
|
final result = handler.processPlayerDeath(
|
||||||
state,
|
state,
|
||||||
@@ -108,8 +107,8 @@ void main() {
|
|||||||
|
|
||||||
group('보스전(boss fight) 사망', () {
|
group('보스전(boss fight) 사망', () {
|
||||||
test('보스전 사망 시 장비가 보호된다 (lostCount == 0)', () {
|
test('보스전 사망 시 장비가 보호된다 (lostCount == 0)', () {
|
||||||
final equipment = _fullyEquipped();
|
final equipment = fullyEquipped();
|
||||||
final state = _createState(
|
final state = createState(
|
||||||
level: 10,
|
level: 10,
|
||||||
isBossFight: true,
|
isBossFight: true,
|
||||||
equipment: equipment,
|
equipment: equipment,
|
||||||
@@ -136,7 +135,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('보스전 사망 시 bossLevelingEndTime이 설정된다 (5분)', () {
|
test('보스전 사망 시 bossLevelingEndTime이 설정된다 (5분)', () {
|
||||||
final state = _createState(isBossFight: true);
|
final state = createState(isBossFight: true);
|
||||||
final before = DateTime.now().millisecondsSinceEpoch;
|
final before = DateTime.now().millisecondsSinceEpoch;
|
||||||
|
|
||||||
final result = handler.processPlayerDeath(
|
final result = handler.processPlayerDeath(
|
||||||
@@ -157,7 +156,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('일반 사망 시 bossLevelingEndTime은 null이다', () {
|
test('일반 사망 시 bossLevelingEndTime은 null이다', () {
|
||||||
final state = _createState(level: 1);
|
final state = createState(level: 1);
|
||||||
|
|
||||||
final result = handler.processPlayerDeath(
|
final result = handler.processPlayerDeath(
|
||||||
state,
|
state,
|
||||||
@@ -185,7 +184,7 @@ void main() {
|
|||||||
}
|
}
|
||||||
expect(lossySeed, isNotNull, reason: '장비 손실 시드를 찾을 수 없음');
|
expect(lossySeed, isNotNull, reason: '장비 손실 시드를 찾을 수 없음');
|
||||||
|
|
||||||
final state = _createState(seed: lossySeed!, level: 1);
|
final state = createState(seed: lossySeed!, level: 1);
|
||||||
final result = handler.processPlayerDeath(
|
final result = handler.processPlayerDeath(
|
||||||
state,
|
state,
|
||||||
killerName: 'Goblin',
|
killerName: 'Goblin',
|
||||||
@@ -208,7 +207,7 @@ void main() {
|
|||||||
}
|
}
|
||||||
expect(safeSeed, isNotNull, reason: '장비 보호 시드를 찾을 수 없음');
|
expect(safeSeed, isNotNull, reason: '장비 보호 시드를 찾을 수 없음');
|
||||||
|
|
||||||
final state = _createState(seed: safeSeed!, level: 1);
|
final state = createState(seed: safeSeed!, level: 1);
|
||||||
final result = handler.processPlayerDeath(
|
final result = handler.processPlayerDeath(
|
||||||
state,
|
state,
|
||||||
killerName: 'Goblin',
|
killerName: 'Goblin',
|
||||||
@@ -221,7 +220,7 @@ void main() {
|
|||||||
|
|
||||||
test('Lv10+: 100% 확률로 장비 손실', () {
|
test('Lv10+: 100% 확률로 장비 손실', () {
|
||||||
// Lv10 이상은 항상 100% 손실
|
// Lv10 이상은 항상 100% 손실
|
||||||
final state = _createState(seed: 42, level: 10);
|
final state = createState(seed: 42, level: 10);
|
||||||
|
|
||||||
final result = handler.processPlayerDeath(
|
final result = handler.processPlayerDeath(
|
||||||
state,
|
state,
|
||||||
@@ -249,7 +248,7 @@ void main() {
|
|||||||
// Lv10(100% 손실)으로 여러 시드를 반복 테스트
|
// Lv10(100% 손실)으로 여러 시드를 반복 테스트
|
||||||
// 무기는 절대 사라지지 않아야 함
|
// 무기는 절대 사라지지 않아야 함
|
||||||
for (var s = 0; s < 50; s++) {
|
for (var s = 0; s < 50; s++) {
|
||||||
final state = _createState(seed: s, level: 10);
|
final state = createState(seed: s, level: 10);
|
||||||
final result = handler.processPlayerDeath(
|
final result = handler.processPlayerDeath(
|
||||||
state,
|
state,
|
||||||
killerName: 'Monster',
|
killerName: 'Monster',
|
||||||
@@ -280,7 +279,7 @@ void main() {
|
|||||||
// 무기만 있고 나머지는 빈 장비
|
// 무기만 있고 나머지는 빈 장비
|
||||||
final emptyEquipment = Equipment.empty();
|
final emptyEquipment = Equipment.empty();
|
||||||
// Equipment.empty()는 기본 무기(Keyboard)만 있음
|
// Equipment.empty()는 기본 무기(Keyboard)만 있음
|
||||||
final state = _createState(
|
final state = createState(
|
||||||
seed: 42,
|
seed: 42,
|
||||||
level: 10, // 100% 손실 확률
|
level: 10, // 100% 손실 확률
|
||||||
equipment: emptyEquipment,
|
equipment: emptyEquipment,
|
||||||
@@ -300,7 +299,7 @@ void main() {
|
|||||||
|
|
||||||
group('deathCount 증가', () {
|
group('deathCount 증가', () {
|
||||||
test('사망 시 deathCount가 1 증가한다', () {
|
test('사망 시 deathCount가 1 증가한다', () {
|
||||||
final state = _createState();
|
final state = createState();
|
||||||
expect(state.progress.deathCount, 0);
|
expect(state.progress.deathCount, 0);
|
||||||
|
|
||||||
final result = handler.processPlayerDeath(
|
final result = handler.processPlayerDeath(
|
||||||
@@ -314,7 +313,7 @@ void main() {
|
|||||||
|
|
||||||
test('연속 사망 시 deathCount가 누적된다', () {
|
test('연속 사망 시 deathCount가 누적된다', () {
|
||||||
// 첫 번째 사망 (deathCount: 0 -> 1)
|
// 첫 번째 사망 (deathCount: 0 -> 1)
|
||||||
var state = _createState(seed: 100);
|
var state = createState(seed: 100);
|
||||||
state = handler.processPlayerDeath(
|
state = handler.processPlayerDeath(
|
||||||
state,
|
state,
|
||||||
killerName: 'Goblin',
|
killerName: 'Goblin',
|
||||||
@@ -338,7 +337,7 @@ void main() {
|
|||||||
group('currentCombat 초기화', () {
|
group('currentCombat 초기화', () {
|
||||||
test('사망 후 currentCombat이 null로 초기화되어야 한다', () {
|
test('사망 후 currentCombat이 null로 초기화되어야 한다', () {
|
||||||
// 전투 중(combat active) 상태에서 사망
|
// 전투 중(combat active) 상태에서 사망
|
||||||
final state = _createState();
|
final state = createState();
|
||||||
expect(state.progress.currentCombat, isNotNull);
|
expect(state.progress.currentCombat, isNotNull);
|
||||||
|
|
||||||
final result = handler.processPlayerDeath(
|
final result = handler.processPlayerDeath(
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
// ignore_for_file: avoid_print
|
||||||
|
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
@@ -7,7 +9,7 @@ import 'package:flutter_test/flutter_test.dart';
|
|||||||
/// 다양한 GCD 값에 대해 전투 효율성을 측정
|
/// 다양한 GCD 값에 대해 전투 효율성을 측정
|
||||||
void main() {
|
void main() {
|
||||||
test('GCD 시뮬레이션 - 다양한 값 비교', () {
|
test('GCD 시뮬레이션 - 다양한 값 비교', () {
|
||||||
print('\n' + '=' * 80);
|
print('\n${'=' * 80}');
|
||||||
print('글로벌 쿨타임(GCD) 시뮬레이션 결과');
|
print('글로벌 쿨타임(GCD) 시뮬레이션 결과');
|
||||||
print('=' * 80);
|
print('=' * 80);
|
||||||
|
|
||||||
@@ -93,14 +95,14 @@ void main() {
|
|||||||
'- DPS 손실이 크지 않음 (${((1 - gcd1500.avgDps / baselineDps) * 100).toStringAsFixed(0)}% ~ ${((1 - gcd2000.avgDps / baselineDps) * 100).toStringAsFixed(0)}%)',
|
'- DPS 손실이 크지 않음 (${((1 - gcd1500.avgDps / baselineDps) * 100).toStringAsFixed(0)}% ~ ${((1 - gcd2000.avgDps / baselineDps) * 100).toStringAsFixed(0)}%)',
|
||||||
);
|
);
|
||||||
|
|
||||||
print('\n' + '=' * 80);
|
print('\n${'=' * 80}');
|
||||||
|
|
||||||
// 테스트는 항상 통과 (정보 출력용)
|
// 테스트는 항상 통과 (정보 출력용)
|
||||||
expect(true, isTrue);
|
expect(true, isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('GCD 상세 시뮬레이션 - 레벨별 영향', () {
|
test('GCD 상세 시뮬레이션 - 레벨별 영향', () {
|
||||||
print('\n' + '=' * 80);
|
print('\n${'=' * 80}');
|
||||||
print('레벨별 GCD 영향 분석');
|
print('레벨별 GCD 영향 분석');
|
||||||
print('=' * 80);
|
print('=' * 80);
|
||||||
|
|
||||||
@@ -153,7 +155,7 @@ void main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
print('\n' + '=' * 80);
|
print('\n${'=' * 80}');
|
||||||
expect(true, isTrue);
|
expect(true, isTrue);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
|
// ignore_for_file: inference_failure_on_collection_literal
|
||||||
|
|
||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:asciineverdie/src/core/model/equipment_container.dart';
|
|
||||||
import 'package:asciineverdie/src/core/model/equipment_item.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/equipment_slot.dart';
|
||||||
import 'package:asciineverdie/src/core/model/item_stats.dart';
|
import 'package:asciineverdie/src/core/model/item_stats.dart';
|
||||||
@@ -17,7 +18,7 @@ void main() {
|
|||||||
// 헬퍼: 모든 필드가 채워진 GameSave 생성
|
// 헬퍼: 모든 필드가 채워진 GameSave 생성
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|
||||||
GameSave _createFullSave() {
|
GameSave createFullSave() {
|
||||||
return GameSave(
|
return GameSave(
|
||||||
version: kSaveVersion,
|
version: kSaveVersion,
|
||||||
rngState: 12345,
|
rngState: 12345,
|
||||||
@@ -144,10 +145,10 @@ void main() {
|
|||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|
||||||
test('toJson() → fromJson() 라운드트립 — 모든 필드 보존', () {
|
test('toJson() → fromJson() 라운드트립 — 모든 필드 보존', () {
|
||||||
final original = _createFullSave();
|
final original = createFullSave();
|
||||||
// jsonEncode → jsonDecode로 순수 Map 변환 (freezed 객체 제거)
|
// jsonEncode → jsonDecode로 순수 Map 변환 (freezed 객체 제거)
|
||||||
final json = jsonDecode(jsonEncode(original.toJson()))
|
final json =
|
||||||
as Map<String, dynamic>;
|
jsonDecode(jsonEncode(original.toJson())) as Map<String, dynamic>;
|
||||||
final restored = GameSave.fromJson(json);
|
final restored = GameSave.fromJson(json);
|
||||||
|
|
||||||
// traits 검증
|
// traits 검증
|
||||||
@@ -184,10 +185,7 @@ void main() {
|
|||||||
expect(restored.equipment.weapon, equals('Flaming Sword'));
|
expect(restored.equipment.weapon, equals('Flaming Sword'));
|
||||||
expect(restored.equipment.shield, equals('Tower Shield'));
|
expect(restored.equipment.shield, equals('Tower Shield'));
|
||||||
expect(restored.equipment.bestIndex, equals(0));
|
expect(restored.equipment.bestIndex, equals(0));
|
||||||
expect(
|
expect(restored.equipment.weaponItem.rarity, equals(ItemRarity.epic));
|
||||||
restored.equipment.weaponItem.rarity,
|
|
||||||
equals(ItemRarity.epic),
|
|
||||||
);
|
|
||||||
expect(restored.equipment.weaponItem.level, equals(10));
|
expect(restored.equipment.weaponItem.level, equals(10));
|
||||||
expect(restored.equipment.weaponItem.stats.atk, equals(50));
|
expect(restored.equipment.weaponItem.stats.atk, equals(50));
|
||||||
|
|
||||||
@@ -201,7 +199,10 @@ void main() {
|
|||||||
expect(restored.progress.task.max, equals(100));
|
expect(restored.progress.task.max, equals(100));
|
||||||
expect(restored.progress.quest.position, equals(3));
|
expect(restored.progress.quest.position, equals(3));
|
||||||
expect(restored.progress.exp.position, equals(500));
|
expect(restored.progress.exp.position, equals(500));
|
||||||
expect(restored.progress.currentTask.caption, equals('Executing a Goblin'));
|
expect(
|
||||||
|
restored.progress.currentTask.caption,
|
||||||
|
equals('Executing a Goblin'),
|
||||||
|
);
|
||||||
expect(restored.progress.currentTask.type, equals(TaskType.kill));
|
expect(restored.progress.currentTask.type, equals(TaskType.kill));
|
||||||
expect(
|
expect(
|
||||||
restored.progress.currentTask.monsterGrade,
|
restored.progress.currentTask.monsterGrade,
|
||||||
@@ -252,7 +253,14 @@ void main() {
|
|||||||
'version': 2,
|
'version': 2,
|
||||||
'rng': 100,
|
'rng': 100,
|
||||||
'traits': {'name': 'OldHero', 'race': 'Elf', 'klass': 'Mage'},
|
'traits': {'name': 'OldHero', 'race': 'Elf', 'klass': 'Mage'},
|
||||||
'stats': {'str': 10, 'con': 10, 'dex': 10, 'int': 10, 'wis': 10, 'cha': 10},
|
'stats': {
|
||||||
|
'str': 10,
|
||||||
|
'con': 10,
|
||||||
|
'dex': 10,
|
||||||
|
'int': 10,
|
||||||
|
'wis': 10,
|
||||||
|
'cha': 10,
|
||||||
|
},
|
||||||
'inventory': {'gold': 500, 'items': []},
|
'inventory': {'gold': 500, 'items': []},
|
||||||
'equipment': {
|
'equipment': {
|
||||||
'weapon': 'Ancient Sword',
|
'weapon': 'Ancient Sword',
|
||||||
@@ -291,7 +299,10 @@ void main() {
|
|||||||
// 레거시 아이템은 level 1, common으로 변환
|
// 레거시 아이템은 level 1, common으로 변환
|
||||||
expect(restored.equipment.weaponItem.level, equals(1));
|
expect(restored.equipment.weaponItem.level, equals(1));
|
||||||
expect(restored.equipment.weaponItem.rarity, equals(ItemRarity.common));
|
expect(restored.equipment.weaponItem.rarity, equals(ItemRarity.common));
|
||||||
expect(restored.equipment.weaponItem.slot, equals(EquipmentSlot.weapon));
|
expect(
|
||||||
|
restored.equipment.weaponItem.slot,
|
||||||
|
equals(EquipmentSlot.weapon),
|
||||||
|
);
|
||||||
|
|
||||||
// 버전 정보 보존
|
// 버전 정보 보존
|
||||||
expect(restored.version, equals(2));
|
expect(restored.version, equals(2));
|
||||||
@@ -299,7 +310,9 @@ void main() {
|
|||||||
|
|
||||||
test('v2 세이브에 monetization 없으면 null', () {
|
test('v2 세이브에 monetization 없으면 null', () {
|
||||||
// jsonDecode로 순수 Map<String, dynamic> 생성 (타입 캐스팅 호환)
|
// jsonDecode로 순수 Map<String, dynamic> 생성 (타입 캐스팅 호환)
|
||||||
final legacyJson = jsonDecode(jsonEncode({
|
final legacyJson =
|
||||||
|
jsonDecode(
|
||||||
|
jsonEncode({
|
||||||
'version': 2,
|
'version': 2,
|
||||||
'rng': 0,
|
'rng': 0,
|
||||||
'traits': {},
|
'traits': {},
|
||||||
@@ -322,7 +335,9 @@ void main() {
|
|||||||
'skills': [],
|
'skills': [],
|
||||||
'progress': {},
|
'progress': {},
|
||||||
'queue': [],
|
'queue': [],
|
||||||
})) as Map<String, dynamic>;
|
}),
|
||||||
|
)
|
||||||
|
as Map<String, dynamic>;
|
||||||
|
|
||||||
final restored = GameSave.fromJson(legacyJson);
|
final restored = GameSave.fromJson(legacyJson);
|
||||||
|
|
||||||
@@ -404,10 +419,7 @@ void main() {
|
|||||||
'plot': {'pos': 0, 'max': 1},
|
'plot': {'pos': 0, 'max': 1},
|
||||||
'exp': {'pos': 0, 'max': 1},
|
'exp': {'pos': 0, 'max': 1},
|
||||||
'encumbrance': {'pos': 0, 'max': 1},
|
'encumbrance': {'pos': 0, 'max': 1},
|
||||||
'taskInfo': {
|
'taskInfo': {'caption': 'Unknown task', 'type': 'nonexistent_type'},
|
||||||
'caption': 'Unknown task',
|
|
||||||
'type': 'nonexistent_type',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
'queue': [
|
'queue': [
|
||||||
{
|
{
|
||||||
@@ -424,10 +436,7 @@ void main() {
|
|||||||
// 알 수 없는 enum 값은 기본값으로 대체
|
// 알 수 없는 enum 값은 기본값으로 대체
|
||||||
expect(restored.progress.currentTask.type, equals(TaskType.neutral));
|
expect(restored.progress.currentTask.type, equals(TaskType.neutral));
|
||||||
expect(restored.queue.entries.first.kind, equals(QueueKind.task));
|
expect(restored.queue.entries.first.kind, equals(QueueKind.task));
|
||||||
expect(
|
expect(restored.queue.entries.first.taskType, equals(TaskType.neutral));
|
||||||
restored.queue.entries.first.taskType,
|
|
||||||
equals(TaskType.neutral),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -448,9 +457,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('skills가 빈 배열이면 빈 스킬북', () {
|
test('skills가 빈 배열이면 빈 스킬북', () {
|
||||||
final json = <String, dynamic>{
|
final json = <String, dynamic>{'skills': []};
|
||||||
'skills': [],
|
|
||||||
};
|
|
||||||
|
|
||||||
final restored = GameSave.fromJson(json);
|
final restored = GameSave.fromJson(json);
|
||||||
|
|
||||||
@@ -494,9 +501,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('queue가 빈 배열이면 빈 큐', () {
|
test('queue가 빈 배열이면 빈 큐', () {
|
||||||
final json = <String, dynamic>{
|
final json = <String, dynamic>{'queue': []};
|
||||||
'queue': [],
|
|
||||||
};
|
|
||||||
|
|
||||||
final restored = GameSave.fromJson(json);
|
final restored = GameSave.fromJson(json);
|
||||||
|
|
||||||
@@ -504,9 +509,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('monetization null이면 null 유지', () {
|
test('monetization null이면 null 유지', () {
|
||||||
final json = <String, dynamic>{
|
final json = <String, dynamic>{'monetization': null};
|
||||||
'monetization': null,
|
|
||||||
};
|
|
||||||
|
|
||||||
final restored = GameSave.fromJson(json);
|
final restored = GameSave.fromJson(json);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user