feat(ui): 화면 및 공통 위젯 개선

- FrontScreen 개선
- GamePlayScreen, GameSessionController 업데이트
- ArenaBattleScreen, NewCharacterScreen 정리
- AsciiDisintegrateWidget 추가
This commit is contained in:
JiWoong Sul
2026-01-14 00:18:16 +09:00
parent f65bab6312
commit 1da377c127
7 changed files with 302 additions and 30 deletions

View File

@@ -10,7 +10,7 @@ import 'package:asciineverdie/src/core/model/hall_of_fame.dart';
import 'package:asciineverdie/src/core/animation/race_character_frames.dart';
import 'package:asciineverdie/src/features/arena/widgets/arena_combat_log.dart';
import 'package:asciineverdie/src/features/arena/widgets/arena_result_panel.dart';
import 'package:asciineverdie/src/features/arena/widgets/ascii_disintegrate_widget.dart';
import 'package:asciineverdie/src/shared/widgets/ascii_disintegrate_widget.dart';
import 'package:asciineverdie/src/features/game/widgets/ascii_animation_card.dart';
import 'package:asciineverdie/src/features/game/widgets/combat_log.dart';
import 'package:asciineverdie/src/shared/retro_colors.dart';

View File

@@ -10,7 +10,7 @@ import 'package:asciineverdie/src/features/front/widgets/hero_vs_boss_animation.
import 'package:asciineverdie/src/shared/retro_colors.dart';
import 'package:asciineverdie/src/shared/widgets/retro_widgets.dart';
class FrontScreen extends StatelessWidget {
class FrontScreen extends StatefulWidget {
const FrontScreen({
super.key,
this.onNewCharacter,
@@ -20,6 +20,8 @@ class FrontScreen extends StatelessWidget {
this.hasSaveFile = false,
this.savedGamePreview,
this.hallOfFameCount = 0,
this.routeObserver,
this.onRefresh,
});
/// "New character" 버튼 클릭 시 호출
@@ -43,12 +45,45 @@ class FrontScreen extends StatelessWidget {
/// 명예의 전당 캐릭터 수 (아레나 활성화 조건: 2명 이상)
final int hallOfFameCount;
/// RouteObserver (화면 복귀 시 갱신용)
final RouteObserver<ModalRoute<void>>? routeObserver;
/// 화면 복귀 시 호출할 콜백
final VoidCallback? onRefresh;
@override
State<FrontScreen> createState() => _FrontScreenState();
}
class _FrontScreenState extends State<FrontScreen> with RouteAware {
@override
void didChangeDependencies() {
super.didChangeDependencies();
// RouteObserver 구독
final route = ModalRoute.of(context);
if (route != null) {
widget.routeObserver?.subscribe(this, route);
}
}
@override
void dispose() {
widget.routeObserver?.unsubscribe(this);
super.dispose();
}
@override
void didPopNext() {
// 다른 화면에서 돌아왔을 때 갱신
widget.onRefresh?.call();
}
/// 새 캐릭터 생성 시 세이브 파일 존재하면 경고 표시
void _handleNewCharacter(BuildContext context) {
if (hasSaveFile) {
if (widget.hasSaveFile) {
_showDeleteWarningDialog(context);
} else {
onNewCharacter?.call(context);
widget.onNewCharacter?.call(context);
}
}
@@ -67,7 +102,7 @@ class FrontScreen extends StatelessWidget {
FilledButton(
onPressed: () {
Navigator.pop(dialogContext);
onNewCharacter?.call(context);
widget.onNewCharacter?.call(context);
},
child: Text(game_l10n.buttonConfirm),
),
@@ -98,21 +133,21 @@ class FrontScreen extends StatelessWidget {
const _AnimationPanel(),
const SizedBox(height: 16),
_ActionButtons(
onNewCharacter: onNewCharacter != null
onNewCharacter: widget.onNewCharacter != null
? () => _handleNewCharacter(context)
: null,
onLoadSave: onLoadSave != null
? () => onLoadSave!(context)
onLoadSave: widget.onLoadSave != null
? () => widget.onLoadSave!(context)
: null,
onHallOfFame: onHallOfFame != null
? () => onHallOfFame!(context)
onHallOfFame: widget.onHallOfFame != null
? () => widget.onHallOfFame!(context)
: null,
onLocalArena:
onLocalArena != null && hallOfFameCount >= 2
? () => onLocalArena!(context)
onLocalArena: widget.onLocalArena != null &&
widget.hallOfFameCount >= 2
? () => widget.onLocalArena!(context)
: null,
savedGamePreview: savedGamePreview,
hallOfFameCount: hallOfFameCount,
savedGamePreview: widget.savedGamePreview,
hallOfFameCount: widget.hallOfFameCount,
),
],
),
@@ -262,16 +297,14 @@ class _ActionButtons extends StatelessWidget {
onPressed: onHallOfFame,
isPrimary: false,
),
// 로컬 아레나 (2명 이상일 때만 활성화)
if (hallOfFameCount >= 2) ...[
const SizedBox(height: 12),
RetroTextButton(
text: game_l10n.uiLocalArena,
icon: Icons.sports_kabaddi,
onPressed: onLocalArena,
isPrimary: false,
),
],
// 로컬 아레나 (항상 표시, 2명 이상일 때만 활성화)
const SizedBox(height: 12),
RetroTextButton(
text: game_l10n.uiLocalArena,
icon: Icons.sports_kabaddi,
onPressed: hallOfFameCount >= 2 ? onLocalArena : null,
isPrimary: false,
),
],
),
);

View File

@@ -1181,6 +1181,7 @@ class _GamePlayScreenState extends State<GamePlayScreen>
state.progress.currentCombat?.monsterStats.hpCurrent,
monsterHpMax: state.progress.currentCombat?.monsterStats.hpMax,
monsterName: state.progress.currentCombat?.monsterStats.name,
monsterLevel: state.progress.currentCombat?.monsterStats.level,
),
// Experience 바

View File

@@ -107,6 +107,14 @@ class GameSessionController extends ChangeNotifier {
if (isNewGame) {
_sessionStats = SessionStatistics.empty();
await _statisticsStorage.recordGameStart();
} else {
// 게임 로드 시 저장된 사망 횟수 복원
_sessionStats = _sessionStats.copyWith(
deathCount: state.progress.deathCount,
questsCompleted: state.progress.questCount,
monstersKilled: state.progress.monstersKilled,
playTimeMs: state.skillSystem.elapsedMs,
);
}
_initPreviousValues(state);
@@ -341,7 +349,7 @@ class GameSessionController extends ChangeNotifier {
final entry = HallOfFameEntry.fromGameState(
state: _state!,
totalDeaths: _sessionStats.deathCount,
totalDeaths: _state!.progress.deathCount, // GameState에 저장된 값 사용
monstersKilled: _state!.progress.monstersKilled,
combatStats: combatStats,
);

View File

@@ -259,7 +259,7 @@ class _NewCharacterScreenState extends State<NewCharacterScreen> {
seed: gameSeed,
traits: traits,
stats: finalStats,
inventory: const Inventory(gold: 0, items: []),
inventory: Inventory.empty(),
equipment: Equipment.empty(),
skillBook: SkillBook.empty(),
progress: ProgressState.empty(),