From a6ba3d5d2e0c677f0c39fb95d04956db82c09d1c Mon Sep 17 00:00:00 2001 From: JiWoong Sul Date: Wed, 17 Dec 2025 17:54:07 +0900 Subject: [PATCH] =?UTF-8?q?feat(phase7):=20=EA=B3=A0=EC=A0=95=204=EC=83=89?= =?UTF-8?q?=20=ED=8C=94=EB=A0=88=ED=8A=B8=20=EC=8B=9C=EC=8A=A4=ED=85=9C=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ascii_colors.dart 생성 - 흰색(object): 캐릭터, 몬스터, 아이템 - 시안(positive): 힐, 버프, 레벨업, 획득 - 마젠타(negative): 데미지, 디버프, 사망, 손실 - 검정(background): 배경 - 테마 선택 기능 제거 - AsciiAnimationCard: colorTheme 파라미터 제거, 고정 색상 사용 - TaskProgressPanel: 테마 버튼 제거 - GamePlayScreen: 테마 관련 상태/메서드 제거 - 이펙트 색상 시스템 업데이트 - '*' (히트) → 마젠타 - '!' '+' (강조/버프) → 시안 - '~' (디버프) → 마젠타 --- lib/src/core/constants/ascii_colors.dart | 81 +++++++++++++++++++ lib/src/features/game/game_play_screen.dart | 32 +------- .../game/widgets/ascii_animation_card.dart | 31 ++++--- .../game/widgets/task_progress_panel.dart | 49 +---------- 4 files changed, 103 insertions(+), 90 deletions(-) create mode 100644 lib/src/core/constants/ascii_colors.dart diff --git a/lib/src/core/constants/ascii_colors.dart b/lib/src/core/constants/ascii_colors.dart new file mode 100644 index 0000000..056e997 --- /dev/null +++ b/lib/src/core/constants/ascii_colors.dart @@ -0,0 +1,81 @@ +import 'package:flutter/material.dart'; + +/// ASCII 애니메이션 4색 팔레트 (Phase 7) +/// +/// 시각적 명확성을 위해 4가지 색상만 사용한다. +/// - 흰색: 오브젝트 (캐릭터, 몬스터, 아이템) +/// - 시안: 포지티브 이펙트 (힐, 버프, 레벨업, 획득) +/// - 마젠타: 네거티브 이펙트 (데미지, 디버프, 사망, 손실) +/// - 검정: 배경 +class AsciiColors { + AsciiColors._(); + + /// 오브젝트 색상 (캐릭터, 몬스터, 아이템) + static const Color object = Colors.white; + + /// 포지티브 이펙트 색상 (힐, 버프, 레벨업, 획득) + static const Color positive = Colors.cyan; + + /// 네거티브 이펙트 색상 (데미지, 디버프, 사망, 손실) + static const Color negative = Color(0xFFFF00FF); // 마젠타 + + /// 배경 색상 + static const Color background = Colors.black; + + /// 상황에 따른 색상 반환 + static Color forContext(AsciiColorContext context) { + return switch (context) { + AsciiColorContext.idle => object, + AsciiColorContext.attack => object, + AsciiColorContext.critical => negative, + AsciiColorContext.heal => positive, + AsciiColorContext.buff => positive, + AsciiColorContext.debuff => negative, + AsciiColorContext.levelUp => positive, + AsciiColorContext.death => negative, + AsciiColorContext.itemGain => positive, + AsciiColorContext.itemLoss => negative, + AsciiColorContext.dodge => object, + AsciiColorContext.block => object, + }; + } +} + +/// ASCII 애니메이션 색상 컨텍스트 +enum AsciiColorContext { + /// 대기 상태 + idle, + + /// 일반 공격 + attack, + + /// 크리티컬 히트 + critical, + + /// 회복 + heal, + + /// 버프 획득 + buff, + + /// 디버프 적용 + debuff, + + /// 레벨업 + levelUp, + + /// 사망 + death, + + /// 아이템 획득 + itemGain, + + /// 아이템 손실 + itemLoss, + + /// 회피 성공 + dodge, + + /// 방패 방어 + block, +} diff --git a/lib/src/features/game/game_play_screen.dart b/lib/src/features/game/game_play_screen.dart index ec686fd..4190e32 100644 --- a/lib/src/features/game/game_play_screen.dart +++ b/lib/src/features/game/game_play_screen.dart @@ -1,16 +1,16 @@ import 'package:flutter/material.dart'; import 'package:askiineverdie/l10n/app_localizations.dart'; -import 'package:askiineverdie/src/core/animation/ascii_animation_data.dart'; import 'package:askiineverdie/src/core/animation/ascii_animation_type.dart'; import 'package:askiineverdie/src/core/l10n/game_data_l10n.dart'; import 'package:askiineverdie/src/core/model/game_state.dart'; -import 'package:askiineverdie/src/core/storage/theme_preferences.dart'; import 'package:askiineverdie/src/core/util/pq_logic.dart' as pq_logic; import 'package:askiineverdie/src/features/game/game_session_controller.dart'; import 'package:askiineverdie/src/features/game/widgets/task_progress_panel.dart'; /// 게임 진행 화면 (Main.dfm 기반 3패널 레이아웃) +/// +/// Phase 7: colorTheme 제거됨, 고정 4색 팔레트 사용 class GamePlayScreen extends StatefulWidget { const GamePlayScreen({super.key, required this.controller}); @@ -22,7 +22,6 @@ class GamePlayScreen extends StatefulWidget { class _GamePlayScreenState extends State with WidgetsBindingObserver { - AsciiColorTheme _colorTheme = AsciiColorTheme.green; AsciiAnimationType? _specialAnimation; // 이전 상태 추적 (레벨업/퀘스트/Act 완료 감지용) @@ -30,28 +29,6 @@ class _GamePlayScreenState extends State int _lastQuestCount = 0; int _lastPlotStageCount = 0; - void _cycleColorTheme() { - setState(() { - _colorTheme = switch (_colorTheme) { - AsciiColorTheme.green => AsciiColorTheme.amber, - AsciiColorTheme.amber => AsciiColorTheme.white, - AsciiColorTheme.white => AsciiColorTheme.system, - AsciiColorTheme.system => AsciiColorTheme.green, - }; - }); - // 테마 변경 시 저장 - ThemePreferences.saveColorTheme(_colorTheme); - } - - Future _loadColorTheme() async { - final theme = await ThemePreferences.loadColorTheme(); - if (mounted) { - setState(() { - _colorTheme = theme; - }); - } - } - void _checkSpecialEvents(GameState state) { // 레벨업 감지 if (state.traits.level > _lastLevel && _lastLevel > 0) { @@ -92,7 +69,6 @@ class _GamePlayScreenState extends State super.initState(); widget.controller.addListener(_onControllerChanged); WidgetsBinding.instance.addObserver(this); - _loadColorTheme(); // 초기 상태 설정 final state = widget.controller.state; @@ -216,7 +192,7 @@ class _GamePlayScreenState extends State ), body: Column( children: [ - // 상단: ASCII 애니메이션 + Task Progress + // 상단: ASCII 애니메이션 + Task Progress (Phase 7: 고정 4색 팔레트) TaskProgressPanel( progress: state.progress, speedMultiplier: widget.controller.loop?.speedMultiplier ?? 1, @@ -224,8 +200,6 @@ class _GamePlayScreenState extends State widget.controller.loop?.cycleSpeed(); setState(() {}); }, - colorTheme: _colorTheme, - onThemeCycle: _cycleColorTheme, isPaused: !widget.controller.isRunning, onPauseToggle: () async { await widget.controller.togglePause(); diff --git a/lib/src/features/game/widgets/ascii_animation_card.dart b/lib/src/features/game/widgets/ascii_animation_card.dart index 3cda676..bc358e0 100644 --- a/lib/src/features/game/widgets/ascii_animation_card.dart +++ b/lib/src/features/game/widgets/ascii_animation_card.dart @@ -9,6 +9,7 @@ import 'package:askiineverdie/src/core/animation/battle_composer.dart'; import 'package:askiineverdie/src/core/animation/character_frames.dart'; import 'package:askiineverdie/src/core/animation/monster_size.dart'; import 'package:askiineverdie/src/core/animation/weapon_category.dart'; +import 'package:askiineverdie/src/core/constants/ascii_colors.dart'; import 'package:askiineverdie/src/core/model/game_state.dart'; /// ASCII 애니메이션 카드 위젯 @@ -336,31 +337,26 @@ class _AsciiAnimationCardState extends State { return TextSpan(children: spans); } - /// 이펙트 문자별 색상 반환 + /// 이펙트 문자별 색상 반환 (Phase 7: 4색 팔레트) Color _getEffectColor(String char) { return switch (char) { - '*' => Colors.orange, // 히트/폭발 - '!' => Colors.yellow, // 강조 - '=' || '>' || '<' => Colors.cyan, // 슬래시/찌르기 - '~' => Colors.purple, // 물결/마법 - _ => Colors.white, + '*' => AsciiColors.negative, // 히트/폭발 (마젠타) + '!' => AsciiColors.positive, // 강조 (시안) + '=' || '>' || '<' => AsciiColors.positive, // 슬래시/찌르기 (시안) + '~' => AsciiColors.negative, // 물결/디버프 (마젠타) + '+' => AsciiColors.positive, // 회복/버프 (시안) + _ => AsciiColors.object, // 오브젝트 (흰색) }; } @override Widget build(BuildContext context) { - final brightness = Theme.of(context).brightness; - final colors = getThemeColors(widget.colorTheme, brightness); - - // 특수 애니메이션 중이면 특별한 배경색 적용 - final isSpecial = _currentSpecialAnimation != null; - final bgColor = isSpecial - ? colors.backgroundColor.withValues(alpha: 0.95) - : colors.backgroundColor; + // Phase 7: 고정 4색 팔레트 사용 (colorTheme 무시) + const bgColor = AsciiColors.background; + const textColor = AsciiColors.object; // 프레임 텍스트 결정 String frameText; - Color textColor = colors.textColor; if (_isBattleMode && _battleComposer != null) { // 새 배틀 시스템 사용 (배경 포함) @@ -380,13 +376,16 @@ class _AsciiAnimationCardState extends State { frameText = _animationData.frames[frameIndex]; } + // 특수 애니메이션 중이면 테두리 표시 + final isSpecial = _currentSpecialAnimation != null; + return Container( padding: const EdgeInsets.all(4), decoration: BoxDecoration( color: bgColor, borderRadius: BorderRadius.circular(4), border: isSpecial - ? Border.all(color: colors.textColor.withValues(alpha: 0.5)) + ? Border.all(color: AsciiColors.positive.withValues(alpha: 0.5)) : null, ), child: _isBattleMode diff --git a/lib/src/features/game/widgets/task_progress_panel.dart b/lib/src/features/game/widgets/task_progress_panel.dart index 9b5166c..18ddf5e 100644 --- a/lib/src/features/game/widgets/task_progress_panel.dart +++ b/lib/src/features/game/widgets/task_progress_panel.dart @@ -1,20 +1,19 @@ import 'package:flutter/material.dart'; import 'package:askiineverdie/l10n/app_localizations.dart'; -import 'package:askiineverdie/src/core/animation/ascii_animation_data.dart'; import 'package:askiineverdie/src/core/animation/ascii_animation_type.dart'; import 'package:askiineverdie/src/core/model/game_state.dart'; import 'package:askiineverdie/src/features/game/widgets/ascii_animation_card.dart'; /// 상단 패널: ASCII 애니메이션 + Task Progress 바 +/// +/// Phase 7: colorTheme 제거됨, 고정 4색 팔레트 사용 class TaskProgressPanel extends StatelessWidget { const TaskProgressPanel({ super.key, required this.progress, required this.speedMultiplier, required this.onSpeedCycle, - required this.colorTheme, - required this.onThemeCycle, required this.isPaused, required this.onPauseToggle, this.specialAnimation, @@ -27,8 +26,6 @@ class TaskProgressPanel extends StatelessWidget { final ProgressState progress; final int speedMultiplier; final VoidCallback onSpeedCycle; - final AsciiColorTheme colorTheme; - final VoidCallback onThemeCycle; /// 일시 정지 상태 final bool isPaused; @@ -56,13 +53,12 @@ class TaskProgressPanel extends StatelessWidget { child: Column( mainAxisSize: MainAxisSize.min, children: [ - // ASCII 애니메이션 카드 + // ASCII 애니메이션 카드 (Phase 7: 고정 4색 팔레트) SizedBox( height: 120, child: AsciiAnimationCard( taskType: progress.currentTask.type, monsterBaseName: progress.currentTask.monsterBaseName, - colorTheme: colorTheme, specialAnimation: specialAnimation, weaponName: weaponName, shieldName: shieldName, @@ -73,11 +69,9 @@ class TaskProgressPanel extends StatelessWidget { ), const SizedBox(height: 8), - // 상태 메시지 + 버튼들 + // 상태 메시지 + 버튼들 (Phase 7: 테마 버튼 제거됨) Row( children: [ - _buildThemeButton(context), - const SizedBox(width: 4), _buildPauseButton(context), const SizedBox(width: 8), Expanded( @@ -102,41 +96,6 @@ class TaskProgressPanel extends StatelessWidget { ); } - Widget _buildThemeButton(BuildContext context) { - final themeLabel = switch (colorTheme) { - AsciiColorTheme.green => 'G', - AsciiColorTheme.amber => 'A', - AsciiColorTheme.white => 'W', - AsciiColorTheme.system => 'S', - }; - - final themeColor = switch (colorTheme) { - AsciiColorTheme.green => const Color(0xFF00FF00), - AsciiColorTheme.amber => const Color(0xFFFFB000), - AsciiColorTheme.white => Colors.white, - AsciiColorTheme.system => Theme.of(context).colorScheme.primary, - }; - - return SizedBox( - height: 28, - child: OutlinedButton( - onPressed: onThemeCycle, - style: OutlinedButton.styleFrom( - padding: const EdgeInsets.symmetric(horizontal: 8), - visualDensity: VisualDensity.compact, - side: BorderSide(color: themeColor.withValues(alpha: 0.5)), - ), - child: Text( - themeLabel, - style: TextStyle( - fontWeight: FontWeight.bold, - color: themeColor, - ), - ), - ), - ); - } - Widget _buildPauseButton(BuildContext context) { return SizedBox( height: 28,