import 'package:flutter/material.dart'; import 'package:asciineverdie/src/shared/animation/ascii_animation_type.dart'; /// ASCII 애니메이션 프레임 데이터 class AsciiAnimationData { const AsciiAnimationData({required this.frames, this.frameIntervalMs = 200}); /// 각 프레임 (문자열, 최소 5줄) final List frames; /// 프레임 간격 (밀리초) final int frameIntervalMs; } /// 터미널 색상 테마 enum AsciiColorTheme { /// 클래식 녹색 터미널 green, /// 엠버 (호박색) 터미널 amber, /// 화이트 온 블랙 white, /// 시스템 테마 (라이트/다크 모드 따름) system, } /// 테마별 색상 데이터 class AsciiThemeColors { const AsciiThemeColors({ required this.textColor, required this.backgroundColor, }); final Color textColor; final Color backgroundColor; } /// 테마별 색상 반환 AsciiThemeColors getThemeColors(AsciiColorTheme theme, Brightness brightness) { return switch (theme) { AsciiColorTheme.green => const AsciiThemeColors( textColor: Color(0xFF00FF00), backgroundColor: Color(0xFF0D0D0D), ), AsciiColorTheme.amber => const AsciiThemeColors( textColor: Color(0xFFFFB000), backgroundColor: Color(0xFF1A1000), ), AsciiColorTheme.white => const AsciiThemeColors( textColor: Color(0xFFE0E0E0), backgroundColor: Color(0xFF121212), ), AsciiColorTheme.system => brightness == Brightness.dark ? const AsciiThemeColors( textColor: Color(0xFFE0E0E0), backgroundColor: Color(0xFF1E1E1E), ) : const AsciiThemeColors( textColor: Color(0xFF1E1E1E), backgroundColor: Color(0xFFF5F5F5), ), }; } /// 몬스터 카테고리 (ASCII NEVER DIE 테마) enum MonsterCategory { /// 기본 버그 (Syntax Error, Type Mismatch 등) bug, /// 멀웨어 (Virus, Worm, Trojan 등) malware, /// 네트워크 위협 (Flood, DDoS, Injection 등) network, /// 시스템 위협 (Kernel, Memory, Buffer 등) system, /// 암호화/보안 (Encryption, Hash, Zero-Day 등) crypto, /// AI/ML 위협 (Neural Network, Machine Learning 등) ai, /// 보스 몬스터 boss, } /// 몬스터 이름으로 카테고리 결정 (ASCII NEVER DIE 테마) MonsterCategory getMonsterCategory(String? monsterBaseName) { if (monsterBaseName == null || monsterBaseName.isEmpty) { return MonsterCategory.bug; } final name = monsterBaseName.toLowerCase(); // 보스 몬스터 if (name.startsWith('boss:') || name.contains('dragon') || name.contains('hydra') || name.contains('titan') || name.contains('leviathan') || name.contains('colossus') || name.contains('emperor') || name.contains('singularity') || name.contains('primordial') || name.contains('glitch god')) { return MonsterCategory.boss; } // AI/ML 위협 if (name.contains('neural') || name.contains('ai ') || name.contains('deep') || name.contains('model') || name.contains('adversarial') || name.contains('training') || name.contains('federated') || name.contains('prompt') || name.contains('hallucination')) { return MonsterCategory.ai; } // 암호화/보안 위협 if (name.contains('quantum') || name.contains('zero day') || name.contains('zero-day') || name.contains('crypto') || name.contains('hash') || name.contains('blockchain') || name.contains('homomorphic') || name.contains('zero knowledge') || name.contains('smart contract')) { return MonsterCategory.crypto; } // 네트워크 위협 if (name.contains('flood') || name.contains('ddos') || name.contains('dos') || name.contains('injection') || name.contains('sql') || name.contains('xss') || name.contains('csrf') || name.contains('amplification') || name.contains('tunnel') || name.contains('shell') || name.contains('backdoor') || name.contains('c2') || name.contains('beacon')) { return MonsterCategory.network; } // 시스템 위협 if (name.contains('kernel') || name.contains('memory') || name.contains('buffer') || name.contains('stack') || name.contains('heap') || name.contains('overflow') || name.contains('corruption') || name.contains('segfault') || name.contains('panic') || name.contains('rootkit') || name.contains('firmware') || name.contains('bootkit')) { return MonsterCategory.system; } // 멀웨어 if (name.contains('virus') || name.contains('worm') || name.contains('trojan') || name.contains('ransomware') || name.contains('malware') || name.contains('botnet') || name.contains('cryptominer') || name.contains('keylogger') || name.contains('spyware') || name.contains('dropper') || name.contains('loader') || name.contains('payload')) { return MonsterCategory.malware; } // 기본: 버그 return MonsterCategory.bug; } /// 버그 전투 애니메이션 (기본 버그 모양) const battleAnimationBug = AsciiAnimationData( frames: [ // 프레임 1: 대치 ''' o /\\_/\\ /|\\ ( o.o ) / \\ > ^ <''', // 프레임 2: 접근 ''' o /\\_/\\ /|\\ ( o.o ) / \\ > ^ <''', // 프레임 3: 공격 (근접) ''' o_/ /\\_/\\ /| ( >.< ) / \\ > ^ <''', // 프레임 4: 히트 ''' o **** /\\_/\\ /|\\ *** ( X.X ) / \\ > ~ <''', // 프레임 5: 복귀 (승리 포즈) ''' \\o/ /\\_/\\ /|\\ ( -.-) / \\ > ^ <''', ], frameIntervalMs: 220, ); /// 마을/상점 애니메이션 (8줄 x 40자 고정) /// 캐릭터 위치: 머리=4, 몸통=5, 다리=6 (전투 애니메이션 기준) const townAnimation = AsciiAnimationData( frames: [ // 프레임 1: 상점 앞 대기 ' ___________ \n' ' / SHOP \\ \n' ' | [======] | \n' ' | @@@@ | \n' ' | ITEMS | o \n' ' | | /|\\ \n' ' |___________| / \\ \n' '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', // 프레임 2: 이동 중 ' ___________ \n' ' / SHOP \\ \n' ' | [======] | \n' ' | @@@@ | \n' ' | ITEMS | o \n' ' | | /|\\ \n' ' |___________| / \\ \n' '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', // 프레임 3: 거래 시작 ' ___________ \n' ' / SHOP \\ \n' ' | [======] | \$ \n' ' | @@@@ | \$ \n' ' | ITEMS | o \$ \n' ' | | /|\\ \n' ' |___________| / \\ \n' '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', // 프레임 4: 거래 중 ' ___________ \n' ' / SHOP \\ \n' ' | [<====>] | \$\$\$ \n' ' | @@@@ | \$\$\$ \n' ' | SOLD! | o \n' ' | | /|\\ \n' ' |___________| / \\ \n' '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', // 프레임 5: 거래 완료 ' ___________ \n' ' / SHOP \\ \n' ' | [======] | + \n' ' | @@@@ | + \n' ' | ITEMS | \\o/ \n' ' | | /|\\ \n' ' |___________| / \\ \n' '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', ], frameIntervalMs: 280, ); /// 걷는 애니메이션 (8줄 x 40자 고정) const walkingAnimation = AsciiAnimationData( frames: [ // 프레임 1: 양발 벌림 ' \n' ' \n' ' \n' ' \n' ' o \n' ' /|\\ \n' ' / \\ \n' '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', // 프레임 2: 왼발 앞으로 ' \n' ' \n' ' \n' ' \n' ' o \n' ' /|\\ \n' ' /| \n' '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', // 프레임 3: 두 발 모음 ' \n' ' \n' ' \n' ' \n' ' o \n' ' /|\\ \n' ' || \n' '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', // 프레임 4: 오른발 앞으로 ' \n' ' \n' ' \n' ' \n' ' o \n' ' /|\\ \n' ' |\\ \n' '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', // 프레임 5: 양발 벌림 (복귀) ' \n' ' \n' ' \n' ' \n' ' o \n' ' /|\\ \n' ' / \\ \n' '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', ], frameIntervalMs: 180, ); /// 멀웨어 전투 애니메이션 (바이러스 모양) const battleAnimationMalware = AsciiAnimationData( frames: [ // 프레임 1: 대치 ''' o /\\_/\\ /|\\ ( o o ) / \\ /|=====|\\''', // 프레임 2: 접근 ''' o /\\_/\\ /|\\ ( o o ) / \\ /|=====|\\''', // 프레임 3: 공격 (근접) ''' o_/ /\\_/\\ /| ( >.< ) / \\ /|=====|\\''', // 프레임 4: 히트 ''' o **** /\\_/\\ /|\\*** ( X X ) / \\ /|=====|\\''', // 프레임 5: 복귀 (승리 포즈) ''' \\o/ /\\_/\\ /|\\ ( - - ) / \\ /|=====|\\''', ], frameIntervalMs: 220, ); /// 네트워크 전투 애니메이션 (패킷 모양) const battleAnimationNetwork = AsciiAnimationData( frames: [ // 프레임 1: 대치 ''' o O /|\\ /|\\ / \\ / | \\''', // 프레임 2: 접근 ''' o O /|\\ /|\\ / \\ / | \\''', // 프레임 3: 공격 (근접) ''' o_/ O /| X|X / \\ / | \\''', // 프레임 4: 히트 ''' o **** O /|\\ *** X|X / \\ / | \\''', // 프레임 5: 복귀 (승리 포즈) ''' \\o/ O /|\\ /|\\ / \\ / | \\''', ], frameIntervalMs: 220, ); /// 시스템 전투 애니메이션 (커널 모양) const battleAnimationSystem = AsciiAnimationData( frames: [ // 프레임 1: 대치 ''' o .-. /|\\ (o.o) / \\ |=|''', // 프레임 2: 접근 ''' o .-. /|\\ (o.o) / \\ |=|''', // 프레임 3: 공격 (근접) ''' o_/ .-. /| (>.>) / \\ |=|''', // 프레임 4: 히트 ''' o **** .-. /|\\*** (X.X) / \\ |~|''', // 프레임 5: 복귀 (승리 포즈) ''' \\o/ .-. /|\\ (-.-) / \\ |=|''', ], frameIntervalMs: 250, ); /// 암호화 전투 애니메이션 (자물쇠 모양) const battleAnimationCrypto = AsciiAnimationData( frames: [ // 프레임 1: 대치 ''' o __/\\__ /|\\ < (O)(O) > / \\ \\ \\/ /''', // 프레임 2: 접근 ''' o __/\\__ /|\\ < (O)(O) > / \\ \\ \\/ /''', // 프레임 3: 공격 (근접) ''' o_/ __/\\__ /| < (X)(X) > / \\ \\ \\/ /''', // 프레임 4: 히트 ''' o **** __/\\__ /|\\***< (X)(X) > / \\ \\ ~~ /''', // 프레임 5: 복귀 (승리 포즈) ''' \\o/ __/\\__ /|\\ < (-)(-)> / \\ \\ \\/ /''', ], frameIntervalMs: 200, ); /// AI 전투 애니메이션 (뉴럴넷 모양) const battleAnimationAI = AsciiAnimationData( frames: [ // 프레임 1: 대치 ''' o .---. /|\\ ( o o ) / \\ ~~~~~''', // 프레임 2: 접근 ''' o .---. /|\\ ( o o ) / \\ ~~~~~''', // 프레임 3: 공격 (근접) ''' o_/ .---. /| ( >.< ) / \\ ~~~~~''', // 프레임 4: 히트 ''' o **** .---. /|\\** ( X X ) / \\ ~~~~~''', // 프레임 5: 복귀 (승리 포즈) ''' \\o/ .---. /|\\ ( - - ) / \\ ~~~~~''', ], frameIntervalMs: 280, ); /// 보스 전투 애니메이션 (드래곤/보스 모양) const battleAnimationBoss = AsciiAnimationData( frames: [ // 프레임 1: 대치 ''' o /\\ /\\ /|\\ ( o V o ) / \\ \\ ~~~ /''', // 프레임 2: 접근 ''' o /\\ /\\ /|\\ ( o V o ) / \\ \\ ~~~ /''', // 프레임 3: 공격 (근접) ''' o_/ /\\ /\\ /| ( X V X ) / \\ \\ ~~~ /''', // 프레임 4: 히트 ''' o ****/\\ /\\ /|\\** ( X V X ) / \\ \\ ~~~ /''', // 프레임 5: 복귀 (승리 포즈) ''' \\o/ /\\ /\\ /|\\ ( - V - ) / \\ \\ ~~~ /''', ], frameIntervalMs: 200, ); /// 몬스터 카테고리별 전투 애니메이션 반환 AsciiAnimationData getBattleAnimation(MonsterCategory category) { return switch (category) { MonsterCategory.bug => battleAnimationBug, MonsterCategory.malware => battleAnimationMalware, MonsterCategory.network => battleAnimationNetwork, MonsterCategory.system => battleAnimationSystem, MonsterCategory.crypto => battleAnimationCrypto, MonsterCategory.ai => battleAnimationAI, MonsterCategory.boss => battleAnimationBoss, }; } /// 레벨업 축하 애니메이션 (8줄 x 40자 고정) /// 캐릭터 위치: 머리=4, 몸통=5, 다리=6 (전투 애니메이션 기준) const levelUpAnimation = AsciiAnimationData( frames: [ // 프레임 1: 시작 ' * * * \n' ' * * * \n' ' * * \n' ' \n' ' * \\O/ * \n' ' /|\\ \n' ' * / \\ * \n' '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', // 프레임 2: 별 확산 ' * * * \n' ' * * \n' ' * * \n' ' \n' ' * \\O/ * \n' ' /|\\ \n' ' * / \\ * \n' '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', // 프레임 3: 레벨업 텍스트 ' * L E V E L U P ! * \n' ' * * \n' ' * * \n' ' \n' ' * \\O/ * \n' ' /|\\ \n' ' * / \\ * \n' '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', // 프레임 4: 빛나는 캐릭터 ' * * * * * \n' ' * * \n' ' * * * * \n' ' \n' ' * \\O/ * \n' ' * /|\\ * \n' ' * / \\ * \n' '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', // 프레임 5: 마무리 ' + \n' ' +++ \n' ' +++++ \n' ' \n' ' \\O/ \n' ' /|\\ \n' ' / \\ \n' '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', ], frameIntervalMs: 300, ); /// 퀘스트 완료 애니메이션 (8줄 x 40자 고정) /// 캐릭터 위치: 머리=4, 몸통=5, 다리=6 (전투 애니메이션 기준) const questCompleteAnimation = AsciiAnimationData( frames: [ // 프레임 1: 퀘스트 깃발 ' [=======] \n' ' || || \n' ' || || \n' ' ||_____|| \n' ' \\O/ \n' ' /|\\ \n' ' / \\ \n' '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', // 프레임 2: 승리 ' [QUEST!] \n' ' || || \n' ' || || \n' ' ||_____|| \n' ' \\\\O// \n' ' /|\\ \n' ' / \\ \n' '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', // 프레임 3: 보상 ' COMPLETE! \n' ' \n' ' \n' ' \$\$\$ \n' ' \\O/ \$\$\$ \n' ' /|\\ \$\$\$ \n' ' / \\ \n' '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', // 프레임 4: 축하 ' * * * * * \n' ' \n' ' \n' ' * * * * * \n' ' \\O/ +EXP \n' ' /|\\ +GOLD \n' ' / \\ \n' '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', // 프레임 5: 마무리 ' [ VICTORY! ] \n' ' \n' ' \n' ' \n' ' \\O/ \n' ' /|\\ \n' ' / \\ \n' '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', ], frameIntervalMs: 350, ); /// Act 완료 애니메이션 (8줄 x 40자 고정) /// 캐릭터 위치: 머리=4, 몸통=5, 다리=6 (전투 애니메이션 기준) const actCompleteAnimation = AsciiAnimationData( frames: [ // 프레임 1: 커튼 ' ______________________________ \n' ' | A C T | \n' ' | C O M P L E T E | \n' ' |______________________________| \n' ' \n' ' \n' ' \n' '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', // 프레임 2: 캐릭터 등장 ' ______________________________ \n' ' | * * * * * | \n' ' | * * * * * | \n' ' |______________________________| \n' ' \\O/ \n' ' /|\\ \n' ' / \\ \n' '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', // 프레임 3: 플롯 진행 표시 ' ______________________________ \n' ' | PROLOGUE --> ACT | \n' ' | STORY CONTINUES --> | \n' ' |______________________________| \n' ' \\O/ \n' ' /|\\ \n' ' / \\ \n' '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', // 프레임 4: 축하 ' ______________________________ \n' ' | * * * * * | \n' ' | * * * * * | \n' ' |______________________________| \n' ' * \\O/ * \n' ' /|\\ \n' ' * / \\ * \n' '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', // 프레임 5: 마무리 ' ______________________________ \n' ' | +---------+ | \n' ' | | NEXT | | \n' ' |____| CHAPTER |_______________| \n' ' \\O/ \n' ' /|\\ \n' ' / \\ \n' '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', ], frameIntervalMs: 400, ); /// 부활 애니메이션 (8줄 x 40자 고정) /// 어둠에서 빛으로, 캐릭터가 다시 일어남 const resurrectionAnimation = AsciiAnimationData( frames: [ // 프레임 1: 어둠 ' \n' ' . . . . . . \n' ' . . \n' ' . R . I . P . \n' ' . ___ . \n' ' . |___| . \n' ' . . . . . . \n' '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', // 프레임 2: 빛이 비침 ' * \n' ' . .|. . . . \n' ' . | . \n' ' . | . \n' ' . _O_ . \n' ' . /___\\ . \n' ' . . . . . . \n' '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', // 프레임 3: 일어나는 중 ' * * * \n' ' . | . . . \n' ' . | . \n' ' . O . \n' ' . /|\\ . \n' ' . | | . \n' ' . . . . . . \n' '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', // 프레임 4: 서있음 ' * * * * * \n' ' \n' ' \n' ' O \n' ' /|\\ \n' ' / \\ \n' ' \n' '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', // 프레임 5: 부활 완료 ' * RESURRECTED! * \n' ' \n' ' \n' ' \\O/ \n' ' /|\\ \n' ' / \\ \n' ' \n' '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~', ], frameIntervalMs: 600, // 5프레임 × 600ms = 3초 ); /// 타입별 애니메이션 데이터 반환 (기본 전투는 bug) AsciiAnimationData getAnimationData(AsciiAnimationType type) { return switch (type) { AsciiAnimationType.battle => battleAnimationBug, AsciiAnimationType.town => townAnimation, AsciiAnimationType.walking => walkingAnimation, AsciiAnimationType.levelUp => levelUpAnimation, AsciiAnimationType.questComplete => questCompleteAnimation, AsciiAnimationType.actComplete => actCompleteAnimation, AsciiAnimationType.resurrection => resurrectionAnimation, }; }