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:
JiWoong Sul
2026-03-31 14:22:09 +09:00
parent 068d9da4bd
commit 68a5848510
10 changed files with 126 additions and 116 deletions

View File

@@ -14,6 +14,9 @@ analyzer:
strict-casts: true
strict-inference: true
strict-raw-types: true
exclude:
- "**/*.freezed.dart"
- "**/*.g.dart"
linter:
# Keep the rule set lean; we will tighten as the engine port stabilizes.

View File

@@ -159,7 +159,7 @@ String get speedBoostTitle => _l('Speed Boost', '속도 부스트', 'スピー
String get speedBoostActivate =>
_l('Activate 10x Speed', '10배속 활성화', '10倍速を有効化');
String speedBoostRemaining(int seconds) =>
_l('${seconds}s remaining', '${seconds} 남음', '残り${seconds}');
_l('${seconds}s remaining', '$seconds 남음', '残り$seconds秒');
String get speedBoostActive => _l('BOOST ACTIVE', '부스트 활성화', 'ブースト中');
// ============================================================================
@@ -650,8 +650,9 @@ String translateImpressiveTitle(String englishName) {
/// 특수 아이템 이름 번역
String translateSpecial(String englishName) {
if (isKoreanLocale) return specialTranslationsKo[englishName] ?? englishName;
if (isJapaneseLocale)
if (isJapaneseLocale) {
return specialTranslationsJa[englishName] ?? englishName;
}
return englishName;
}
@@ -828,54 +829,65 @@ String translateItemNameL10n(String itemString) {
/// Act 제목 번역
String translateActTitle(String englishTitle) {
if (isKoreanLocale)
if (isKoreanLocale) {
return actTitleTranslationsKo[englishTitle] ?? englishTitle;
if (isJapaneseLocale)
}
if (isJapaneseLocale) {
return actTitleTranslationsJa[englishTitle] ?? englishTitle;
}
return englishTitle;
}
/// Act 보스 이름 번역
String translateActBoss(String englishBoss) {
if (isKoreanLocale) return actBossTranslationsKo[englishBoss] ?? englishBoss;
if (isJapaneseLocale)
if (isJapaneseLocale) {
return actBossTranslationsJa[englishBoss] ?? englishBoss;
}
return englishBoss;
}
/// Act 퀘스트 번역
String translateActQuest(String englishQuest) {
if (isKoreanLocale)
if (isKoreanLocale) {
return actQuestTranslationsKo[englishQuest] ?? englishQuest;
if (isJapaneseLocale)
}
if (isJapaneseLocale) {
return actQuestTranslationsJa[englishQuest] ?? englishQuest;
}
return englishQuest;
}
/// 시네마틱 텍스트 번역
String translateCinematic(String englishText) {
if (isKoreanLocale)
if (isKoreanLocale) {
return cinematicTranslationsKo[englishText] ?? englishText;
if (isJapaneseLocale)
}
if (isJapaneseLocale) {
return cinematicTranslationsJa[englishText] ?? englishText;
}
return englishText;
}
/// 지역 이름 번역
String translateLocation(String englishLocation) {
if (isKoreanLocale)
if (isKoreanLocale) {
return locationTranslationsKo[englishLocation] ?? englishLocation;
if (isJapaneseLocale)
}
if (isJapaneseLocale) {
return locationTranslationsJa[englishLocation] ?? englishLocation;
}
return englishLocation;
}
/// 세력/조직 이름 번역
String translateFaction(String englishFaction) {
if (isKoreanLocale)
if (isKoreanLocale) {
return factionTranslationsKo[englishFaction] ?? englishFaction;
if (isJapaneseLocale)
}
if (isJapaneseLocale) {
return factionTranslationsJa[englishFaction] ?? englishFaction;
}
return englishFaction;
}
@@ -1233,7 +1245,7 @@ String get notifyQuestComplete => _l('QUEST COMPLETE!', '퀘스트 완료!', '
String get notifyPrologueComplete =>
_l('PROLOGUE COMPLETE!', '프롤로그 완료!', 'プロローグ完了!');
String notifyActComplete(int actNumber) =>
_l('ACT $actNumber COMPLETE!', '${actNumber} 완료!', '${actNumber}幕完了!');
_l('ACT $actNumber COMPLETE!', '$actNumber 완료!', '$actNumber幕完了');
String get notifyNewSpell => _l('NEW SPELL!', '새 주문!', '新しい呪文!');
String get notifyNewEquipment => _l('NEW EQUIPMENT!', '새 장비!', '新しい装備!');
String get notifyBossDefeated => _l('BOSS DEFEATED!', '보스 처치!', 'ボス撃破!');

View File

@@ -1,8 +1,6 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/foundation.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:in_app_purchase/in_app_purchase.dart';
@@ -190,10 +188,7 @@ class IAPService implements IIAPService {
/// 구매 상태 저장 (보안 저장소 사용)
Future<void> _savePurchaseState(bool purchased) async {
await _secureStorage.write(
key: _purchaseKey,
value: purchased.toString(),
);
await _secureStorage.write(key: _purchaseKey, value: purchased.toString());
_adRemovalPurchased = purchased;
debugPrint('[IAPService] Saved purchase state: $purchased');
}
@@ -451,10 +446,7 @@ class IAPService implements IIAPService {
pc.PublicKeyParameter<pc.RSAPublicKey>(publicKey),
);
return signer.verifySignature(
dataBytes,
pc.RSASignature(signatureBytes),
);
return signer.verifySignature(dataBytes, pc.RSASignature(signatureBytes));
} catch (e) {
debugPrint('[IAPService] RSA verification error: $e');
return false;
@@ -477,10 +469,8 @@ class IAPService implements IIAPService {
final rsaParser = ASN1Parser(publicKeyBytes);
final rsaSequence = rsaParser.nextObject() as ASN1Sequence;
final modulus =
(rsaSequence.elements![0] as ASN1Integer).integer!;
final exponent =
(rsaSequence.elements![1] as ASN1Integer).integer!;
final modulus = (rsaSequence.elements![0] as ASN1Integer).integer!;
final exponent = (rsaSequence.elements![1] as ASN1Integer).integer!;
return pc.RSAPublicKey(modulus, exponent);
}

View File

@@ -2,6 +2,7 @@
///
/// 유틸리티 함수 모음.
/// 이 파일은 분할된 모듈들을 re-export하여 기존 코드 호환성 유지.
library;
// 랜덤/확률 함수
export 'package:asciineverdie/src/core/util/pq_random.dart';

View File

@@ -52,7 +52,7 @@ class SavePickerDialog extends StatelessWidget {
child: ListView.separated(
shrinkWrap: true,
itemCount: saves.length,
separatorBuilder: (_, __) => const Divider(height: 1),
separatorBuilder: (_, _) => const Divider(height: 1),
itemBuilder: (context, index) {
final save = saves[index];
return _SaveListTile(

View File

@@ -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/src/shared/l10n/game_data_l10n.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/desktop_panel_widgets.dart';
import 'package:asciineverdie/src/features/game/widgets/equipment_stats_panel.dart';

View File

@@ -2,6 +2,7 @@
///
/// CanvasBattleComposer에서 분리된 전투 텍스트 프레임 상수.
/// 크리티컬, 회피, 미스, 디버프, DOT, 블록, 패리 텍스트 프레임.
library;
// ============================================================================
// 몬스터 공격 이펙트 (← 방향, Phase 8) - 5줄

View File

@@ -1,5 +1,4 @@
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_slot.dart';
import 'package:asciineverdie/src/core/model/game_state.dart';
@@ -13,7 +12,7 @@ void main() {
const handler = DeathHandler();
/// 테스트용 장비 생성 헬퍼
EquipmentItem _makeItem(String name, EquipmentSlot slot) {
EquipmentItem makeItem(String name, EquipmentSlot slot) {
return EquipmentItem(
name: name,
slot: slot,
@@ -25,20 +24,20 @@ void main() {
}
/// 모든 슬롯에 장비가 장착된 Equipment 생성
Equipment _fullyEquipped() {
Equipment fullyEquipped() {
return Equipment(
items: [
EquipmentItem.defaultWeapon(), // 0: 무기(weapon)
_makeItem('Iron Shield', EquipmentSlot.shield),
_makeItem('Iron Helm', EquipmentSlot.helm),
_makeItem('Iron Hauberk', EquipmentSlot.hauberk),
_makeItem('Iron Brassairts', EquipmentSlot.brassairts),
_makeItem('Iron Vambraces', EquipmentSlot.vambraces),
_makeItem('Iron Gauntlets', EquipmentSlot.gauntlets),
_makeItem('Iron Gambeson', EquipmentSlot.gambeson),
_makeItem('Iron Cuisses', EquipmentSlot.cuisses),
_makeItem('Iron Greaves', EquipmentSlot.greaves),
_makeItem('Iron Sollerets', EquipmentSlot.sollerets),
makeItem('Iron Shield', EquipmentSlot.shield),
makeItem('Iron Helm', EquipmentSlot.helm),
makeItem('Iron Hauberk', EquipmentSlot.hauberk),
makeItem('Iron Brassairts', EquipmentSlot.brassairts),
makeItem('Iron Vambraces', EquipmentSlot.vambraces),
makeItem('Iron Gauntlets', EquipmentSlot.gauntlets),
makeItem('Iron Gambeson', EquipmentSlot.gambeson),
makeItem('Iron Cuisses', EquipmentSlot.cuisses),
makeItem('Iron Greaves', EquipmentSlot.greaves),
makeItem('Iron Sollerets', EquipmentSlot.sollerets),
],
bestIndex: 0,
);
@@ -50,7 +49,7 @@ void main() {
/// [level]: 캐릭터 레벨
/// [isBossFight]: 보스전(boss fight) 여부
/// [equipment]: 커스텀 장비
GameState _createState({
GameState createState({
int seed = 42,
int level = 1,
bool isBossFight = false,
@@ -59,7 +58,7 @@ void main() {
final state = GameState(
rng: DeterministicRandom(seed),
traits: Traits.empty().copyWith(level: level),
equipment: equipment ?? _fullyEquipped(),
equipment: equipment ?? fullyEquipped(),
progress: ProgressState.empty().copyWith(
currentCombat: MockFactories.createCombat(),
finalBossState: isBossFight
@@ -75,7 +74,7 @@ void main() {
group('deathInfo 생성', () {
test('사망 시 deathInfo가 올바르게 생성된다', () {
// 준비(arrange): Lv10 캐릭터 - 100% 장비 손실 확률
final state = _createState(level: 10);
final state = createState(level: 10);
// 실행(act)
final result = handler.processPlayerDeath(
@@ -94,7 +93,7 @@ void main() {
});
test('selfDamage 원인으로 사망 시 cause가 올바르다', () {
final state = _createState(level: 1);
final state = createState(level: 1);
final result = handler.processPlayerDeath(
state,
@@ -108,8 +107,8 @@ void main() {
group('보스전(boss fight) 사망', () {
test('보스전 사망 시 장비가 보호된다 (lostCount == 0)', () {
final equipment = _fullyEquipped();
final state = _createState(
final equipment = fullyEquipped();
final state = createState(
level: 10,
isBossFight: true,
equipment: equipment,
@@ -136,7 +135,7 @@ void main() {
});
test('보스전 사망 시 bossLevelingEndTime이 설정된다 (5분)', () {
final state = _createState(isBossFight: true);
final state = createState(isBossFight: true);
final before = DateTime.now().millisecondsSinceEpoch;
final result = handler.processPlayerDeath(
@@ -157,7 +156,7 @@ void main() {
});
test('일반 사망 시 bossLevelingEndTime은 null이다', () {
final state = _createState(level: 1);
final state = createState(level: 1);
final result = handler.processPlayerDeath(
state,
@@ -185,7 +184,7 @@ void main() {
}
expect(lossySeed, isNotNull, reason: '장비 손실 시드를 찾을 수 없음');
final state = _createState(seed: lossySeed!, level: 1);
final state = createState(seed: lossySeed!, level: 1);
final result = handler.processPlayerDeath(
state,
killerName: 'Goblin',
@@ -208,7 +207,7 @@ void main() {
}
expect(safeSeed, isNotNull, reason: '장비 보호 시드를 찾을 수 없음');
final state = _createState(seed: safeSeed!, level: 1);
final state = createState(seed: safeSeed!, level: 1);
final result = handler.processPlayerDeath(
state,
killerName: 'Goblin',
@@ -221,7 +220,7 @@ void main() {
test('Lv10+: 100% 확률로 장비 손실', () {
// Lv10 이상은 항상 100% 손실
final state = _createState(seed: 42, level: 10);
final state = createState(seed: 42, level: 10);
final result = handler.processPlayerDeath(
state,
@@ -249,7 +248,7 @@ void main() {
// Lv10(100% 손실)으로 여러 시드를 반복 테스트
// 무기는 절대 사라지지 않아야 함
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(
state,
killerName: 'Monster',
@@ -280,7 +279,7 @@ void main() {
// 무기만 있고 나머지는 빈 장비
final emptyEquipment = Equipment.empty();
// Equipment.empty()는 기본 무기(Keyboard)만 있음
final state = _createState(
final state = createState(
seed: 42,
level: 10, // 100% 손실 확률
equipment: emptyEquipment,
@@ -300,7 +299,7 @@ void main() {
group('deathCount 증가', () {
test('사망 시 deathCount가 1 증가한다', () {
final state = _createState();
final state = createState();
expect(state.progress.deathCount, 0);
final result = handler.processPlayerDeath(
@@ -314,7 +313,7 @@ void main() {
test('연속 사망 시 deathCount가 누적된다', () {
// 첫 번째 사망 (deathCount: 0 -> 1)
var state = _createState(seed: 100);
var state = createState(seed: 100);
state = handler.processPlayerDeath(
state,
killerName: 'Goblin',
@@ -338,7 +337,7 @@ void main() {
group('currentCombat 초기화', () {
test('사망 후 currentCombat이 null로 초기화되어야 한다', () {
// 전투 중(combat active) 상태에서 사망
final state = _createState();
final state = createState();
expect(state.progress.currentCombat, isNotNull);
final result = handler.processPlayerDeath(

View File

@@ -1,3 +1,5 @@
// ignore_for_file: avoid_print
import 'dart:math';
import 'package:flutter_test/flutter_test.dart';
@@ -7,7 +9,7 @@ import 'package:flutter_test/flutter_test.dart';
/// 다양한 GCD 값에 대해 전투 효율성을 측정
void main() {
test('GCD 시뮬레이션 - 다양한 값 비교', () {
print('\n' + '=' * 80);
print('\n${'=' * 80}');
print('글로벌 쿨타임(GCD) 시뮬레이션 결과');
print('=' * 80);
@@ -93,14 +95,14 @@ void main() {
'- DPS 손실이 크지 않음 (${((1 - gcd1500.avgDps / baselineDps) * 100).toStringAsFixed(0)}% ~ ${((1 - gcd2000.avgDps / baselineDps) * 100).toStringAsFixed(0)}%)',
);
print('\n' + '=' * 80);
print('\n${'=' * 80}');
// 테스트는 항상 통과 (정보 출력용)
expect(true, isTrue);
});
test('GCD 상세 시뮬레이션 - 레벨별 영향', () {
print('\n' + '=' * 80);
print('\n${'=' * 80}');
print('레벨별 GCD 영향 분석');
print('=' * 80);
@@ -153,7 +155,7 @@ void main() {
}
}
print('\n' + '=' * 80);
print('\n${'=' * 80}');
expect(true, isTrue);
});
}

View File

@@ -1,7 +1,8 @@
// ignore_for_file: inference_failure_on_collection_literal
import 'dart:collection';
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_slot.dart';
import 'package:asciineverdie/src/core/model/item_stats.dart';
@@ -17,7 +18,7 @@ void main() {
// 헬퍼: 모든 필드가 채워진 GameSave 생성
// =========================================================================
GameSave _createFullSave() {
GameSave createFullSave() {
return GameSave(
version: kSaveVersion,
rngState: 12345,
@@ -144,10 +145,10 @@ void main() {
// =========================================================================
test('toJson() → fromJson() 라운드트립 — 모든 필드 보존', () {
final original = _createFullSave();
final original = createFullSave();
// jsonEncode → jsonDecode로 순수 Map 변환 (freezed 객체 제거)
final json = jsonDecode(jsonEncode(original.toJson()))
as Map<String, dynamic>;
final json =
jsonDecode(jsonEncode(original.toJson())) as Map<String, dynamic>;
final restored = GameSave.fromJson(json);
// traits 검증
@@ -184,10 +185,7 @@ void main() {
expect(restored.equipment.weapon, equals('Flaming Sword'));
expect(restored.equipment.shield, equals('Tower Shield'));
expect(restored.equipment.bestIndex, equals(0));
expect(
restored.equipment.weaponItem.rarity,
equals(ItemRarity.epic),
);
expect(restored.equipment.weaponItem.rarity, equals(ItemRarity.epic));
expect(restored.equipment.weaponItem.level, equals(10));
expect(restored.equipment.weaponItem.stats.atk, equals(50));
@@ -201,7 +199,10 @@ void main() {
expect(restored.progress.task.max, equals(100));
expect(restored.progress.quest.position, equals(3));
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.monsterGrade,
@@ -252,7 +253,14 @@ void main() {
'version': 2,
'rng': 100,
'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': []},
'equipment': {
'weapon': 'Ancient Sword',
@@ -291,7 +299,10 @@ void main() {
// 레거시 아이템은 level 1, common으로 변환
expect(restored.equipment.weaponItem.level, equals(1));
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));
@@ -299,30 +310,34 @@ void main() {
test('v2 세이브에 monetization 없으면 null', () {
// jsonDecode로 순수 Map<String, dynamic> 생성 (타입 캐스팅 호환)
final legacyJson = jsonDecode(jsonEncode({
'version': 2,
'rng': 0,
'traits': {},
'stats': {},
'inventory': {'gold': 0, 'items': []},
'equipment': {
'weapon': 'Keyboard',
'shield': '',
'helm': '',
'hauberk': '',
'brassairts': '',
'vambraces': '',
'gauntlets': '',
'gambeson': '',
'cuisses': '',
'greaves': '',
'sollerets': '',
'bestIndex': 0,
},
'skills': [],
'progress': {},
'queue': [],
})) as Map<String, dynamic>;
final legacyJson =
jsonDecode(
jsonEncode({
'version': 2,
'rng': 0,
'traits': {},
'stats': {},
'inventory': {'gold': 0, 'items': []},
'equipment': {
'weapon': 'Keyboard',
'shield': '',
'helm': '',
'hauberk': '',
'brassairts': '',
'vambraces': '',
'gauntlets': '',
'gambeson': '',
'cuisses': '',
'greaves': '',
'sollerets': '',
'bestIndex': 0,
},
'skills': [],
'progress': {},
'queue': [],
}),
)
as Map<String, dynamic>;
final restored = GameSave.fromJson(legacyJson);
@@ -404,10 +419,7 @@ void main() {
'plot': {'pos': 0, 'max': 1},
'exp': {'pos': 0, 'max': 1},
'encumbrance': {'pos': 0, 'max': 1},
'taskInfo': {
'caption': 'Unknown task',
'type': 'nonexistent_type',
},
'taskInfo': {'caption': 'Unknown task', 'type': 'nonexistent_type'},
},
'queue': [
{
@@ -424,10 +436,7 @@ void main() {
// 알 수 없는 enum 값은 기본값으로 대체
expect(restored.progress.currentTask.type, equals(TaskType.neutral));
expect(restored.queue.entries.first.kind, equals(QueueKind.task));
expect(
restored.queue.entries.first.taskType,
equals(TaskType.neutral),
);
expect(restored.queue.entries.first.taskType, equals(TaskType.neutral));
});
});
@@ -448,9 +457,7 @@ void main() {
});
test('skills가 빈 배열이면 빈 스킬북', () {
final json = <String, dynamic>{
'skills': [],
};
final json = <String, dynamic>{'skills': []};
final restored = GameSave.fromJson(json);
@@ -494,9 +501,7 @@ void main() {
});
test('queue가 빈 배열이면 빈 큐', () {
final json = <String, dynamic>{
'queue': [],
};
final json = <String, dynamic>{'queue': []};
final restored = GameSave.fromJson(json);
@@ -504,9 +509,7 @@ void main() {
});
test('monetization null이면 null 유지', () {
final json = <String, dynamic>{
'monetization': null,
};
final json = <String, dynamic>{'monetization': null};
final restored = GameSave.fromJson(json);