/// 밸런스 상수 정의 (balance constants) /// /// Phase 6: 레벨 1-100 구간의 난이도 곡선 및 스케일링 상수 library; /// 경험치 관련 상수 (experience constants) class ExpConstants { ExpConstants._(); /// 레벨업에 필요한 경험치 계산 (몬스터 기반) /// /// 공식: (10 + level * 5) * (25 + level / 3) /// - 몬스터 경험치와 동기화 (MonsterBaseStats.exp = 10 + level * 5) /// - 레벨당 약 25~58마리 처치 필요 /// /// 예상: /// 레벨 1: 15 * 25 = 375 exp (~25마리) /// 레벨 10: 60 * 28 = 1,680 exp (~28마리) /// 레벨 30: 160 * 35 = 5,600 exp (~35마리) /// 레벨 50: 260 * 42 = 10,920 exp (~42마리) /// 레벨 80: 410 * 52 = 21,320 exp (~52마리) /// 레벨 100: 510 * 58 = 29,580 exp (~58마리) static int requiredExp(int level) { if (level <= 0) return 375; // 해당 레벨 몬스터 경험치 = 10 + level * 5 final monsterExp = 10 + level * 5; // 필요 킬 수 = 25 + level / 3 (레벨이 올라갈수록 약간 더 많이) final killsRequired = 25 + level ~/ 3; return monsterExp * killsRequired; } /// 총 누적 경험치 계산 (특정 레벨까지) static int totalExpToLevel(int level) { int total = 0; for (int i = 1; i < level; i++) { total += requiredExp(i); } return total; } } /// 몬스터 타입 (monster type) enum MonsterType { /// 일반 몬스터 normal, /// 정예 몬스터 (Elite) elite, /// 미니보스 miniboss, /// 보스 boss, /// 최종 보스 finalBoss, } /// 몬스터 타입별 배율 (monster type multipliers) class MonsterTypeMultiplier { const MonsterTypeMultiplier({ required this.hp, required this.atk, required this.def, required this.exp, required this.gold, }); final double hp; final double atk; final double def; final double exp; final double gold; /// 타입별 배율 가져오기 static MonsterTypeMultiplier forType(MonsterType type) { return switch (type) { MonsterType.normal => normal, MonsterType.elite => elite, MonsterType.miniboss => miniboss, MonsterType.boss => boss, MonsterType.finalBoss => finalBoss, }; } /// 일반: 모든 스탯 1.0배 static const normal = MonsterTypeMultiplier( hp: 1.0, atk: 1.0, def: 1.0, exp: 1.0, gold: 1.0, ); /// 정예: HP 2배, ATK 1.3배, DEF 1.2배, EXP 3배 (상향), GOLD 2.5배 static const elite = MonsterTypeMultiplier( hp: 2.0, atk: 1.3, def: 1.2, exp: 3.0, // 2.0 → 3.0 상향 gold: 2.5, ); /// 미니보스: HP 5배, ATK/DEF 1.5배, EXP 8배 (상향), GOLD 6배 static const miniboss = MonsterTypeMultiplier( hp: 5.0, atk: 1.5, def: 1.5, exp: 8.0, // 5.0 → 8.0 상향 gold: 6.0, ); /// 보스: HP 8배 (하향), ATK/DEF 1.8배 (하향), EXP 25배 (상향), GOLD 15배 static const boss = MonsterTypeMultiplier( hp: 8.0, // 10.0 → 8.0 하향 (플레이어 접근성 개선) atk: 1.8, // 2.0 → 1.8 하향 def: 1.8, // 2.0 → 1.8 하향 exp: 25.0, // 15.0 → 25.0 상향 gold: 15.0, ); /// 최종 보스: HP 12배 (하향), ATK/DEF 2.2배 (하향), EXP 80배 (상향), GOLD 50배 static const finalBoss = MonsterTypeMultiplier( hp: 12.0, // 20.0 → 12.0 대폭 하향 (클리어 가능성 확보) atk: 2.2, // 2.5 → 2.2 하향 def: 2.2, // 2.5 → 2.2 하향 exp: 80.0, // 50.0 → 80.0 상향 gold: 50.0, ); } /// 몬스터 기본 스탯 (monster base stats) class MonsterBaseStats { const MonsterBaseStats({ required this.hp, required this.atk, required this.def, required this.exp, required this.gold, }); final int hp; final int atk; final int def; final int exp; final int gold; /// 레벨 기반 기본 스탯 생성 /// /// HP: 50 + level * 20 + (level^2 / 5) /// ATK: 레벨별 차등 적용 /// - 레벨 1~5: 2 + level * 3 (초반 난이도 완화) /// - 레벨 6+: 3 + level * 4 (기존 공식) /// - DEF 감산율 0.5와 연동하여 밸런스 조정 /// - 플레이어가 5~10회 공격 생존 가능하도록 설계 /// DEF: 2 + level * 2 /// EXP: 10 + level * 5 /// GOLD: 5 + level * 3 factory MonsterBaseStats.forLevel(int level) { // 레벨 1~5: 초반 난이도 완화 (2 + level * 3) // 레벨 1=5, 2=8, 3=11, 4=14, 5=17 // 레벨 6+: 기존 공식 (3 + level * 4) // 레벨 6=27, 10=43, 20=83, 50=203, 100=403 final atk = level <= 5 ? 2 + level * 3 : 3 + level * 4; return MonsterBaseStats( hp: 50 + level * 20 + (level * level ~/ 5), atk: atk, def: 2 + level * 2, exp: 10 + level * 5, gold: 5 + level * 3, ); } /// 타입 배율 적용 MonsterBaseStats applyType(MonsterType type) { final mult = MonsterTypeMultiplier.forType(type); return MonsterBaseStats( hp: (hp * mult.hp).round(), atk: (atk * mult.atk).round(), def: (def * mult.def).round(), exp: (exp * mult.exp).round(), gold: (gold * mult.gold).round(), ); } /// 레벨과 타입을 기반으로 몬스터 스탯 생성 static MonsterBaseStats generate(int level, MonsterType type) { return MonsterBaseStats.forLevel(level).applyType(type); } } /// 보스 특수 능력 타입 (boss ability type) enum BossAbilityType { /// 연속 공격 (Syntax Error Dragon) multiAttack, /// HP 회복 (Memory Leak Hydra) regeneration, /// 보호막 (Buffer Overflow Titan) shield, /// 스턴 공격 (Kernel Panic Archon) stunAttack, /// 페이즈 전환 (Glitch God) phaseShift, } /// 보스 스탯 (boss stats) class BossStats extends MonsterBaseStats { const BossStats({ required super.hp, required super.atk, required super.def, required super.exp, required super.gold, required this.phases, required this.enrageThreshold, required this.enrageMultiplier, required this.hasShield, required this.shieldAmount, required this.abilities, }); /// 페이즈 수 (글리치 신은 5페이즈) final int phases; /// 분노 발동 HP % (0.3 = 30%) final double enrageThreshold; /// 분노 시 스탯 배율 final double enrageMultiplier; /// 보호막 보유 여부 final bool hasShield; /// 보호막 수치 final int shieldAmount; /// 특수 능력 목록 final List abilities; /// Syntax Error Dragon (Act I 보스, 레벨 20) static BossStats syntaxErrorDragon(int baseLevel) { final base = MonsterBaseStats.generate(baseLevel, MonsterType.boss); return BossStats( hp: base.hp, atk: base.atk, def: base.def, exp: base.exp, gold: base.gold, phases: 3, enrageThreshold: 0.2, enrageMultiplier: 1.5, hasShield: false, shieldAmount: 0, abilities: [BossAbilityType.multiAttack], ); } /// Memory Leak Hydra (Act II 보스, 레벨 40) static BossStats memoryLeakHydra(int baseLevel) { final base = MonsterBaseStats.generate(baseLevel, MonsterType.boss); return BossStats( hp: base.hp, atk: base.atk, def: base.def, exp: base.exp, gold: base.gold, phases: 2, enrageThreshold: 0.3, enrageMultiplier: 1.3, hasShield: false, shieldAmount: 0, abilities: [BossAbilityType.regeneration], ); } /// Buffer Overflow Titan (Act III 보스, 레벨 60) static BossStats bufferOverflowTitan(int baseLevel) { final base = MonsterBaseStats.generate(baseLevel, MonsterType.boss); return BossStats( hp: base.hp, atk: base.atk, def: base.def, exp: base.exp, gold: base.gold, phases: 2, enrageThreshold: 0.25, enrageMultiplier: 1.4, hasShield: true, shieldAmount: (base.hp * 0.3).round(), abilities: [BossAbilityType.shield], ); } /// Kernel Panic Archon (Act IV 보스, 레벨 80) /// /// Phase 6 밸런스 조정: enrageMultiplier 1.6 → 1.5 static BossStats kernelPanicArchon(int baseLevel) { final base = MonsterBaseStats.generate(baseLevel, MonsterType.boss); return BossStats( hp: base.hp, atk: base.atk, def: base.def, exp: base.exp, gold: base.gold, phases: 3, enrageThreshold: 0.2, enrageMultiplier: 1.5, // 1.6 → 1.5 (분노 시 50% 스탯 증가) hasShield: true, shieldAmount: (base.hp * 0.2).round(), abilities: [BossAbilityType.stunAttack], ); } /// Glitch God (최종 보스, 레벨 100) /// /// Phase 6 밸런스 조정: /// - enrageThreshold: 0.1 → 0.15 (분노 발동 시점 완화) /// - enrageMultiplier: 2.0 → 1.7 (분노 시 스탯 증가 완화) /// - shieldAmount: 50% → 35% (보호막 감소) static BossStats glitchGod(int baseLevel) { final base = MonsterBaseStats.generate(baseLevel, MonsterType.finalBoss); return BossStats( hp: base.hp, atk: base.atk, def: base.def, exp: base.exp, gold: base.gold, phases: 5, enrageThreshold: 0.15, // 0.1 → 0.15 (15% HP에서 분노) enrageMultiplier: 1.7, // 2.0 → 1.7 (분노 시 70% 스탯 증가) hasShield: true, shieldAmount: (base.hp * 0.35).round(), // 0.5 → 0.35 (보호막 30% 감소) abilities: [ BossAbilityType.phaseShift, BossAbilityType.multiAttack, BossAbilityType.regeneration, BossAbilityType.stunAttack, ], ); } } /// 레벨 구간별 설정 (level tier settings) class LevelTierSettings { const LevelTierSettings({ required this.minLevel, required this.maxLevel, required this.name, required this.targetDeathRate, required this.estimatedPlayTimeMinutes, }); final int minLevel; final int maxLevel; final String name; /// 목표 사망 확률 (전투당, 0.01 = 1%) final double targetDeathRate; /// 예상 플레이 시간 (분) final int estimatedPlayTimeMinutes; /// 초반 (레벨 1-20): 튜토리얼 static const early = LevelTierSettings( minLevel: 1, maxLevel: 20, name: '초반', targetDeathRate: 0.02, // 1-3% estimatedPlayTimeMinutes: 90, // 1-2시간 ); /// 중반 (레벨 21-50): 본격적인 성장 static const mid = LevelTierSettings( minLevel: 21, maxLevel: 50, name: '중반', targetDeathRate: 0.04, // 3-5% estimatedPlayTimeMinutes: 240, // 3-5시간 ); /// 후반 (레벨 51-80): 고급 장비 static const late = LevelTierSettings( minLevel: 51, maxLevel: 80, name: '후반', targetDeathRate: 0.075, // 5-10% estimatedPlayTimeMinutes: 390, // 5-8시간 ); /// 엔드게임 (레벨 81-100): 최종 보스 static const endgame = LevelTierSettings( minLevel: 81, maxLevel: 100, name: '엔드게임', targetDeathRate: 0.15, // 10-20% estimatedPlayTimeMinutes: 240, // 3-5시간 ); /// 모든 레벨 구간 static const all = [early, mid, late, endgame]; /// 레벨로 구간 찾기 static LevelTierSettings forLevel(int level) { for (final tier in all) { if (level >= tier.minLevel && level <= tier.maxLevel) { return tier; } } return endgame; } } /// Act별 최소 몬스터 레벨 (act minimum monster level) /// /// 플레이어 레벨과 무관하게 각 Act에서 등장하는 몬스터의 최소 레벨. /// 저레벨 플레이어가 고Act에 도달해도 적절한 난이도 유지. class ActMonsterLevel { ActMonsterLevel._(); /// plotStageCount 기준 최소 몬스터 레벨 /// - 1: Prologue → 1 /// - 2: Act I → 3 /// - 3: Act II → 22 /// - 4: Act III → 45 /// - 5: Act IV → 72 /// - 6: Act V → 88 static const List _minimumLevels = [ 1, // index 0: unused (plotStageCount starts at 1) 1, // Prologue (plotStageCount = 1) 3, // Act I (plotStageCount = 2) 22, // Act II (plotStageCount = 3) 45, // Act III (plotStageCount = 4) 72, // Act IV (plotStageCount = 5) 88, // Act V (plotStageCount = 6) ]; /// plotStageCount에 해당하는 최소 몬스터 레벨 반환 static int forPlotStage(int plotStageCount) { if (plotStageCount < 1) return 1; if (plotStageCount >= _minimumLevels.length) { return _minimumLevels.last; } return _minimumLevels[plotStageCount]; } } /// 플레이어 스탯 스케일링 (player stat scaling) class PlayerScaling { PlayerScaling._(); /// 레벨당 HP 증가량 (12 → 18 상향, 생존율 개선) static const int hpPerLevel = 18; /// 레벨당 MP 증가량 (5 → 6 상향) static const int mpPerLevel = 6; /// CON당 HP 보너스 (6 → 10 상향, 체력 투자 효율 개선) static const int hpPerCon = 10; /// INT당 MP 보너스 (3 → 4 상향) static const int mpPerInt = 4; /// 레벨업 시 HP/MP 계산 static ({int hpMax, int mpMax}) calculateResources({ required int level, required int baseHp, required int baseMp, required int conBonus, required int intBonus, }) { final hpMax = baseHp + (level - 1) * hpPerLevel + conBonus * hpPerCon; final mpMax = baseMp + (level - 1) * mpPerLevel + intBonus * mpPerInt; return (hpMax: hpMax, mpMax: mpMax); } /// 레벨 구간별 ATK 보너스 (후반 DPS 보조) /// - 레벨 60+: +1 ATK per level /// - 레벨 80+: +2 ATK per level static int bonusAtk(int level) { if (level >= 80) return (level - 80) * 2 + 20; if (level >= 60) return level - 60; return 0; } }