- core/animation → shared/animation - core/l10n → shared/l10n - core/constants/ascii_colors → shared/theme/ascii_colors - import 경로 업데이트
761 lines
26 KiB
Dart
761 lines
26 KiB
Dart
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<String> 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,
|
||
};
|
||
}
|