feat(game): 게임 시스템 전면 개편 및 다국어 지원 확장

## 스킬 시스템 개선
- skill_data.dart: 스킬 데이터 구조 전면 개편 (+1176 라인)
- skill_service.dart: 스킬 발동 로직 확장 및 버프 시스템 연동
- skill.dart: 스킬 모델 개선, 쿨다운/효과 타입 추가

## Canvas 애니메이션 리팩토링
- battle_composer.dart 삭제 (레거시 위젯 기반 렌더러)
- monster_colors.dart 삭제 (AsciiCell 색상 시스템으로 통합)
- canvas_battle_composer.dart: z-index 정렬 (몬스터 z=1, 캐릭터 z=2, 이펙트 z=3)
- ascii_cell.dart, ascii_layer.dart: 코드 정리

## UI/UX 개선
- hp_mp_bar.dart: l10n 적용, 몬스터 HP 바 컴팩트화
- death_overlay.dart: 사망 화면 개선
- equipment_stats_panel.dart: 장비 스탯 표시 확장
- active_buff_panel.dart: 버프 패널 개선
- notification_overlay.dart: 알림 시스템 개선

## 다국어 지원 확장
- game_text_l10n.dart: 게임 텍스트 통합 (+758 라인)
- 한국어/일본어/영어/중국어 번역 업데이트
- ARB 파일 동기화

## 게임 로직 개선
- progress_service.dart: 진행 로직 리팩토링
- combat_calculator.dart: 전투 계산 로직 개선
- stat_calculator.dart: 스탯 계산 시스템 개선
- story_service.dart: 스토리 진행 로직 개선

## 기타
- theme_preferences.dart 삭제 (미사용)
- 테스트 파일 업데이트
- class_data.dart: 클래스 데이터 정리
This commit is contained in:
JiWoong Sul
2025-12-22 19:00:58 +09:00
parent f606fca063
commit 99f5b74802
63 changed files with 3403 additions and 2740 deletions

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:askiineverdie/data/game_text_l10n.dart' as l10n;
import 'package:askiineverdie/src/core/engine/item_service.dart';
import 'package:askiineverdie/src/core/model/equipment_item.dart';
import 'package:askiineverdie/src/core/model/equipment_slot.dart';
@@ -135,7 +136,7 @@ class _EmptySlotTile extends StatelessWidget {
contentPadding: const EdgeInsets.symmetric(horizontal: 8),
leading: _SlotIcon(slot: slot, isEmpty: true),
title: Text(
'[${_getSlotName(slot)}] (empty)',
'[${_getSlotName(slot)}] ${l10n.uiEmpty}',
style: TextStyle(
fontSize: 11,
color: Colors.grey.shade600,
@@ -222,10 +223,7 @@ class _TotalScoreHeader extends StatelessWidget {
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.blueGrey.shade700,
Colors.blueGrey.shade600,
],
colors: [Colors.blueGrey.shade700, Colors.blueGrey.shade600],
),
borderRadius: BorderRadius.circular(8),
boxShadow: [
@@ -239,11 +237,7 @@ class _TotalScoreHeader extends StatelessWidget {
child: Row(
children: [
// 장비 아이콘
const Icon(
Icons.shield,
size: 20,
color: Colors.white70,
),
const Icon(Icons.shield, size: 20, color: Colors.white70),
const SizedBox(width: 8),
// 총합 점수
@@ -251,12 +245,9 @@ class _TotalScoreHeader extends StatelessWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Equipment Score',
style: TextStyle(
fontSize: 10,
color: Colors.white70,
),
Text(
l10n.uiEquipmentScore,
style: const TextStyle(fontSize: 10, color: Colors.white70),
),
Text(
'$totalScore',
@@ -304,46 +295,80 @@ class _StatsGrid extends StatelessWidget {
final entries = <_StatEntry>[];
// 공격 스탯
if (stats.atk > 0) entries.add(_StatEntry('ATK', '+${stats.atk}'));
if (stats.magAtk > 0) entries.add(_StatEntry('MATK', '+${stats.magAtk}'));
if (stats.atk > 0) entries.add(_StatEntry(l10n.statAtk, '+${stats.atk}'));
if (stats.magAtk > 0) {
entries.add(_StatEntry(l10n.statMAtk, '+${stats.magAtk}'));
}
if (stats.criRate > 0) {
entries.add(_StatEntry('CRI', '${(stats.criRate * 100).toStringAsFixed(1)}%'));
entries.add(
_StatEntry(l10n.statCri, '${(stats.criRate * 100).toStringAsFixed(1)}%'),
);
}
if (stats.parryRate > 0) {
entries.add(_StatEntry('PARRY', '${(stats.parryRate * 100).toStringAsFixed(1)}%'));
entries.add(
_StatEntry(
l10n.statParry,
'${(stats.parryRate * 100).toStringAsFixed(1)}%',
),
);
}
// 방어 스탯
if (stats.def > 0) entries.add(_StatEntry('DEF', '+${stats.def}'));
if (stats.magDef > 0) entries.add(_StatEntry('MDEF', '+${stats.magDef}'));
if (stats.def > 0) entries.add(_StatEntry(l10n.statDef, '+${stats.def}'));
if (stats.magDef > 0) {
entries.add(_StatEntry(l10n.statMDef, '+${stats.magDef}'));
}
if (stats.blockRate > 0) {
entries.add(_StatEntry('BLOCK', '${(stats.blockRate * 100).toStringAsFixed(1)}%'));
entries.add(
_StatEntry(
l10n.statBlock,
'${(stats.blockRate * 100).toStringAsFixed(1)}%',
),
);
}
if (stats.evasion > 0) {
entries.add(_StatEntry('EVA', '${(stats.evasion * 100).toStringAsFixed(1)}%'));
entries.add(
_StatEntry(l10n.statEva, '${(stats.evasion * 100).toStringAsFixed(1)}%'),
);
}
// 자원 스탯
if (stats.hpBonus > 0) entries.add(_StatEntry('HP', '+${stats.hpBonus}'));
if (stats.mpBonus > 0) entries.add(_StatEntry('MP', '+${stats.mpBonus}'));
if (stats.hpBonus > 0) {
entries.add(_StatEntry(l10n.statHp, '+${stats.hpBonus}'));
}
if (stats.mpBonus > 0) {
entries.add(_StatEntry(l10n.statMp, '+${stats.mpBonus}'));
}
// 능력치 보너스
if (stats.strBonus > 0) entries.add(_StatEntry('STR', '+${stats.strBonus}'));
if (stats.conBonus > 0) entries.add(_StatEntry('CON', '+${stats.conBonus}'));
if (stats.dexBonus > 0) entries.add(_StatEntry('DEX', '+${stats.dexBonus}'));
if (stats.intBonus > 0) entries.add(_StatEntry('INT', '+${stats.intBonus}'));
if (stats.wisBonus > 0) entries.add(_StatEntry('WIS', '+${stats.wisBonus}'));
if (stats.chaBonus > 0) entries.add(_StatEntry('CHA', '+${stats.chaBonus}'));
if (stats.strBonus > 0) {
entries.add(_StatEntry(l10n.statStr, '+${stats.strBonus}'));
}
if (stats.conBonus > 0) {
entries.add(_StatEntry(l10n.statCon, '+${stats.conBonus}'));
}
if (stats.dexBonus > 0) {
entries.add(_StatEntry(l10n.statDex, '+${stats.dexBonus}'));
}
if (stats.intBonus > 0) {
entries.add(_StatEntry(l10n.statInt, '+${stats.intBonus}'));
}
if (stats.wisBonus > 0) {
entries.add(_StatEntry(l10n.statWis, '+${stats.wisBonus}'));
}
if (stats.chaBonus > 0) {
entries.add(_StatEntry(l10n.statCha, '+${stats.chaBonus}'));
}
// 무기 공속
if (slot == EquipmentSlot.weapon && stats.attackSpeed > 0) {
entries.add(_StatEntry('SPEED', '${stats.attackSpeed}ms'));
entries.add(_StatEntry(l10n.statSpeed, '${stats.attackSpeed}ms'));
}
if (entries.isEmpty) {
return const Text(
'No bonus stats',
style: TextStyle(fontSize: 10, color: Colors.grey),
return Text(
l10n.uiNoBonusStats,
style: const TextStyle(fontSize: 10, color: Colors.grey),
);
}
@@ -406,7 +431,7 @@ class _ItemMetaRow extends StatelessWidget {
return Row(
children: [
Text(
'Lv.${item.level}',
l10n.uiLevel(item.level),
style: const TextStyle(fontSize: 9, color: Colors.grey),
),
const SizedBox(width: 8),
@@ -420,7 +445,7 @@ class _ItemMetaRow extends StatelessWidget {
),
const SizedBox(width: 8),
Text(
'Wt.${item.weight}',
l10n.uiWeight(item.weight),
style: const TextStyle(fontSize: 9, color: Colors.grey),
),
],
@@ -441,16 +466,16 @@ class _ItemMetaRow extends StatelessWidget {
/// 슬롯 이름 반환
String _getSlotName(EquipmentSlot slot) {
return switch (slot) {
EquipmentSlot.weapon => 'Weapon',
EquipmentSlot.shield => 'Shield',
EquipmentSlot.helm => 'Helm',
EquipmentSlot.hauberk => 'Hauberk',
EquipmentSlot.brassairts => 'Brassairts',
EquipmentSlot.vambraces => 'Vambraces',
EquipmentSlot.gauntlets => 'Gauntlets',
EquipmentSlot.gambeson => 'Gambeson',
EquipmentSlot.cuisses => 'Cuisses',
EquipmentSlot.greaves => 'Greaves',
EquipmentSlot.sollerets => 'Sollerets',
EquipmentSlot.weapon => l10n.slotWeapon,
EquipmentSlot.shield => l10n.slotShield,
EquipmentSlot.helm => l10n.slotHelm,
EquipmentSlot.hauberk => l10n.slotHauberk,
EquipmentSlot.brassairts => l10n.slotBrassairts,
EquipmentSlot.vambraces => l10n.slotVambraces,
EquipmentSlot.gauntlets => l10n.slotGauntlets,
EquipmentSlot.gambeson => l10n.slotGambeson,
EquipmentSlot.cuisses => l10n.slotCuisses,
EquipmentSlot.greaves => l10n.slotGreaves,
EquipmentSlot.sollerets => l10n.slotSollerets,
};
}