// 캐릭터 애니메이션 프레임 (8줄 Stone Story RPG 스타일) // 참조: Stone Story RPG - 상세하고 생동감 있는 ASCII 아트 /// 전투 페이즈 enum BattlePhase { /// 대치 상태 (기본) idle, /// 공격 준비 prepare, /// 공격 중 attack, /// 피격 (몬스터가 맞음) hit, /// 복귀 recover, } /// 공격자 타입 (위치 계산용) enum AttackerType { /// 공격 없음 (idle 상태) none, /// 플레이어가 공격 player, /// 몬스터가 공격 monster, /// 동시 공격 (양쪽 모두 이동) both, } /// 캐릭터 프레임 데이터 class CharacterFrame { const CharacterFrame(this.lines); /// 프레임 데이터 (3줄) final List lines; /// 방패 오버레이 적용 /// 3줄 캐릭터: [0]=머리, [1]=몸통/팔, [2]=다리 CharacterFrame withShield() { if (lines.length < 2) return this; final newLines = List.from(lines); // 몸통 줄(1번줄, 팔 위치)에 방패 추가 final bodyIdx = 1; if (newLines[bodyIdx].length >= 2) { // 첫 두 문자를 방패로 대체 newLines[bodyIdx] = '[]${newLines[bodyIdx].substring(2)}'; } else { newLines[bodyIdx] = '[]${newLines[bodyIdx]}'; } return CharacterFrame(newLines); } } /// 특정 페이즈와 서브프레임에 해당하는 캐릭터 프레임 반환 CharacterFrame getCharacterFrame(BattlePhase phase, int subFrame) { final frames = switch (phase) { BattlePhase.idle => _idleFrames, BattlePhase.prepare => _prepareFrames, BattlePhase.attack => _attackFrames, BattlePhase.hit => _hitFrames, BattlePhase.recover => _recoverFrames, }; final index = subFrame % frames.length; return frames[index]; } // ============================================================================ // 대기 프레임 (숨쉬기 애니메이션) - 4프레임, 심플 3줄 스타일, 폭 6자 // 구조: [머리, 몸통+팔, 다리] // ============================================================================ const _idleFrames = [ CharacterFrame([r' o ', r' /|\ ', r' / \ ']), CharacterFrame([r' o ', r' /|\ ', r' | | ']), CharacterFrame([r' o ', r' /|\ ', r' / \ ']), CharacterFrame([r' O ', r' /|\ ', r' / \ ']), ]; // ============================================================================ // 준비 프레임 (무기 들기) - 3프레임, 심플 3줄 스타일, 폭 6자 // 구조: [머리, 몸통+팔, 다리] // ============================================================================ const _prepareFrames = [ CharacterFrame([r' o ', r' \|\ ', r' / \ ']), CharacterFrame([r' o_ ', r' \| ', r' / \ ']), CharacterFrame([r' o/ ', r' \| ', r' / \ ']), ]; // ============================================================================ // 공격 프레임 (전진 + 휘두르기) - 5프레임, 심플 3줄 스타일 // 구조: [머리+공격, 몸통+팔, 다리] // 수정: 공격 이펙트를 머리 줄로 통일 (1칸 위로) // ============================================================================ const _attackFrames = [ CharacterFrame([r' o\ ', r' /| ', r' / \ ']), CharacterFrame([r' o- ', r' /| ', r' / \ ']), CharacterFrame([r' o-- ', r' /| ', r' / \ ']), CharacterFrame([r' o-=>', r' /| ', r' / \ ']), CharacterFrame([r' o ', r' /|\ ', r' / \ ']), ]; // ============================================================================ // 히트 프레임 (공격 명중) - 3프레임, 심플 3줄 스타일 // 구조: [머리+이펙트, 몸통+팔, 다리] // 수정: 히트 이펙트를 머리 줄로 통일 (1칸 위로) // ============================================================================ const _hitFrames = [ CharacterFrame([r' o-* ', r' /| ', r' / \ ']), CharacterFrame([r' o=* ', r' /| ', r' / \ ']), CharacterFrame([r' o~* ', r' /| ', r' / \ ']), ]; // ============================================================================ // 복귀 프레임 - 3프레임, 심플 3줄 스타일 // 구조: [머리, 몸통+팔, 다리] // ============================================================================ const _recoverFrames = [ CharacterFrame([r' o ', r' /|\ ', r' | ']), CharacterFrame([r' o ', r' /|\ ', r' / \ ']), CharacterFrame([r' o ', r' /|\ ', r' / \ ']), ];