- game_play_screen에서 desktop 패널 위젯 분리 - death_overlay에서 death_buttons, death_combat_log 분리 - mobile_carousel_layout에서 mobile_options_menu 분리 - 아레나 위젯 개선 (arena_hp_bar, result_panel 등) - settings_screen에서 retro_settings_widgets 분리 - 기타 위젯 리팩토링 및 import 경로 업데이트
213 lines
6.7 KiB
Dart
213 lines
6.7 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
import 'package:asciineverdie/l10n/app_localizations.dart';
|
|
import 'package:asciineverdie/src/core/model/game_state.dart';
|
|
import 'package:asciineverdie/src/core/util/pq_logic.dart' as pq_logic;
|
|
import 'package:asciineverdie/src/core/util/roman.dart' show intToRoman;
|
|
import 'package:asciineverdie/src/features/game/widgets/desktop_panel_widgets.dart';
|
|
import 'package:asciineverdie/src/shared/retro_colors.dart';
|
|
|
|
/// 데스크톱 우측 패널: Plot/Quest
|
|
///
|
|
/// Plot Development, Quests 목록 및 프로그레스 바 표시
|
|
class DesktopQuestPanel extends StatelessWidget {
|
|
const DesktopQuestPanel({super.key, required this.state});
|
|
|
|
final GameState state;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final l10n = L10n.of(context);
|
|
return Card(
|
|
margin: const EdgeInsets.all(4),
|
|
color: RetroColors.panelBg,
|
|
shape: RoundedRectangleBorder(
|
|
side: const BorderSide(
|
|
color: RetroColors.panelBorderOuter,
|
|
width: 2,
|
|
),
|
|
borderRadius: BorderRadius.circular(0),
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
DesktopPanelHeader(title: l10n.plotDevelopment),
|
|
Expanded(child: _PlotList(state: state)),
|
|
DesktopSegmentProgressBar(
|
|
position: state.progress.plot.position,
|
|
max: state.progress.plot.max,
|
|
color: Colors.purple,
|
|
tooltip: state.progress.plot.max > 0
|
|
? '${pq_logic.roughTime(state.progress.plot.max - state.progress.plot.position)} remaining'
|
|
: null,
|
|
),
|
|
DesktopPanelHeader(title: l10n.quests),
|
|
Expanded(child: _QuestList(state: state)),
|
|
DesktopSegmentProgressBar(
|
|
position: state.progress.quest.position,
|
|
max: state.progress.quest.max,
|
|
color: Colors.green,
|
|
tooltip: state.progress.quest.max > 0
|
|
? l10n.percentComplete(
|
|
100 *
|
|
state.progress.quest.position ~/
|
|
state.progress.quest.max,
|
|
)
|
|
: null,
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Plot 목록 위젯
|
|
class _PlotList extends StatelessWidget {
|
|
const _PlotList({required this.state});
|
|
|
|
final GameState state;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final l10n = L10n.of(context);
|
|
final plotCount = state.progress.plotStageCount;
|
|
if (plotCount == 0) {
|
|
return Center(
|
|
child: Text(
|
|
l10n.prologue.toUpperCase(),
|
|
style: const TextStyle(
|
|
fontFamily: 'PressStart2P',
|
|
fontSize: 14,
|
|
color: RetroColors.textDisabled,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
return ListView.builder(
|
|
itemCount: plotCount,
|
|
padding: const EdgeInsets.symmetric(horizontal: 8),
|
|
itemBuilder: (context, index) {
|
|
final isCompleted = index < plotCount - 1;
|
|
final isCurrent = index == plotCount - 1;
|
|
return Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 1),
|
|
child: Row(
|
|
children: [
|
|
Icon(
|
|
isCompleted
|
|
? Icons.check_box
|
|
: (isCurrent
|
|
? Icons.arrow_right
|
|
: Icons.check_box_outline_blank),
|
|
size: 12,
|
|
color: isCompleted
|
|
? RetroColors.expGreen
|
|
: (isCurrent
|
|
? RetroColors.gold
|
|
: RetroColors.textDisabled),
|
|
),
|
|
const SizedBox(width: 4),
|
|
Expanded(
|
|
child: Text(
|
|
index == 0
|
|
? l10n.prologue
|
|
: l10n.actNumber(intToRoman(index)),
|
|
style: TextStyle(
|
|
fontFamily: 'PressStart2P',
|
|
fontSize: 13,
|
|
color: isCompleted
|
|
? RetroColors.textDisabled
|
|
: (isCurrent
|
|
? RetroColors.gold
|
|
: RetroColors.textLight),
|
|
decoration: isCompleted
|
|
? TextDecoration.lineThrough
|
|
: TextDecoration.none,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Quest 목록 위젯
|
|
class _QuestList extends StatelessWidget {
|
|
const _QuestList({required this.state});
|
|
|
|
final GameState state;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final l10n = L10n.of(context);
|
|
final questHistory = state.progress.questHistory;
|
|
|
|
if (questHistory.isEmpty) {
|
|
return Center(
|
|
child: Text(
|
|
l10n.noActiveQuests.toUpperCase(),
|
|
style: const TextStyle(
|
|
fontFamily: 'PressStart2P',
|
|
fontSize: 14,
|
|
color: RetroColors.textDisabled,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
return ListView.builder(
|
|
itemCount: questHistory.length,
|
|
padding: const EdgeInsets.symmetric(horizontal: 8),
|
|
itemBuilder: (context, index) {
|
|
final quest = questHistory[index];
|
|
final isCurrentQuest =
|
|
index == questHistory.length - 1 && !quest.isComplete;
|
|
|
|
return Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 1),
|
|
child: Row(
|
|
children: [
|
|
Icon(
|
|
isCurrentQuest
|
|
? Icons.arrow_right
|
|
: (quest.isComplete
|
|
? Icons.check_box
|
|
: Icons.check_box_outline_blank),
|
|
size: 12,
|
|
color: isCurrentQuest
|
|
? RetroColors.gold
|
|
: (quest.isComplete
|
|
? RetroColors.expGreen
|
|
: RetroColors.textDisabled),
|
|
),
|
|
const SizedBox(width: 4),
|
|
Expanded(
|
|
child: Text(
|
|
quest.caption,
|
|
style: TextStyle(
|
|
fontFamily: 'PressStart2P',
|
|
fontSize: 13,
|
|
color: isCurrentQuest
|
|
? RetroColors.gold
|
|
: (quest.isComplete
|
|
? RetroColors.textDisabled
|
|
: RetroColors.textLight),
|
|
decoration: quest.isComplete
|
|
? TextDecoration.lineThrough
|
|
: TextDecoration.none,
|
|
),
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|