style: dart format 적용

- 전체 Dart 소스 및 테스트 파일 포매팅 통일
- trailing comma, 줄바꿈, 인덴트 정리
This commit is contained in:
JiWoong Sul
2026-02-13 16:08:23 +09:00
parent bccb5cb188
commit d07a0c5554
42 changed files with 460 additions and 422 deletions

View File

@@ -16,9 +16,7 @@ import 'package:asciineverdie/src/core/util/pq_logic.dart' as pq_logic;
///
/// ProgressService에서 추출된 Act 완료, 보스 생성 등의 로직 담당.
class ActProgressionService {
const ActProgressionService({
required this.config,
});
const ActProgressionService({required this.config});
final PqConfig config;

View File

@@ -64,8 +64,10 @@ class CharacterRollService {
_resetUndoForNewSession();
_isInitialized = true;
debugPrint('[CharacterRollService] Initialized: '
'rolls=$_rollsRemaining, undo=$_undoRemaining');
debugPrint(
'[CharacterRollService] Initialized: '
'rolls=$_rollsRemaining, undo=$_undoRemaining',
);
}
/// 저장된 상태 로드
@@ -148,8 +150,10 @@ class CharacterRollService {
// - 무료 유저: 1회 (광고 시청 필요)
_undoRemaining = _isPaidUser ? maxUndoPaidUser : maxUndoFreeUser;
debugPrint('[CharacterRollService] Rolled: remaining=$_rollsRemaining, '
'history=${_rollHistory.length}, undo=$_undoRemaining');
debugPrint(
'[CharacterRollService] Rolled: remaining=$_rollsRemaining, '
'history=${_rollHistory.length}, undo=$_undoRemaining',
);
return true;
}
@@ -214,8 +218,10 @@ class CharacterRollService {
final snapshot = _rollHistory.removeAt(0);
_undoRemaining--;
debugPrint('[CharacterRollService] Undo (paid): '
'remaining=$_undoRemaining, history=${_rollHistory.length}');
debugPrint(
'[CharacterRollService] Undo (paid): '
'remaining=$_undoRemaining, history=${_rollHistory.length}',
);
return snapshot;
}
@@ -241,8 +247,10 @@ class CharacterRollService {
);
if (adResult == AdResult.completed || adResult == AdResult.debugSkipped) {
debugPrint('[CharacterRollService] Undo (free with ad): '
'remaining=$_undoRemaining, history=${_rollHistory.length}');
debugPrint(
'[CharacterRollService] Undo (free with ad): '
'remaining=$_undoRemaining, history=${_rollHistory.length}',
);
return result;
}

View File

@@ -12,7 +12,7 @@ import 'package:asciineverdie/src/core/util/deterministic_random.dart';
/// 상자 내용물 생성 및 오픈 로직 담당
class ChestService {
ChestService({DeterministicRandom? rng})
: _rng = rng ?? DeterministicRandom(DateTime.now().millisecondsSinceEpoch);
: _rng = rng ?? DeterministicRandom(DateTime.now().millisecondsSinceEpoch);
final DeterministicRandom _rng;
@@ -100,7 +100,9 @@ class ChestService {
rarity: rarity,
);
debugPrint('[ChestService] Equipment reward: ${item.name} (${rarity.name})');
debugPrint(
'[ChestService] Equipment reward: ${item.name} (${rarity.name})',
);
return ChestReward.equipment(item);
}
@@ -132,7 +134,10 @@ class ChestService {
ChestReward _generateGoldReward(int playerLevel) {
final baseGold = playerLevel * _goldPerLevel;
final variance = _rng.nextInt(_goldVariance * 2 + 1) - _goldVariance;
final gold = (baseGold + (baseGold * variance / 100)).round().clamp(10, 99999);
final gold = (baseGold + (baseGold * variance / 100)).round().clamp(
10,
99999,
);
debugPrint('[ChestService] Gold reward: $gold');
return ChestReward.gold(gold);
@@ -142,7 +147,10 @@ class ChestService {
ChestReward _generateExperienceReward(int playerLevel) {
final baseExp = playerLevel * _expPerLevel;
final variance = _rng.nextInt(_expVariance * 2 + 1) - _expVariance;
final exp = (baseExp + (baseExp * variance / 100)).round().clamp(10, 999999);
final exp = (baseExp + (baseExp * variance / 100)).round().clamp(
10,
999999,
);
debugPrint('[ChestService] Experience reward: $exp');
return ChestReward.experience(exp);
@@ -208,49 +216,49 @@ class ChestService {
return switch (slot) {
EquipmentSlot.weapon => ItemStats(
atk: baseValue * 2,
criRate: 0.01 * (level ~/ 5),
parryRate: 0.005 * level,
),
atk: baseValue * 2,
criRate: 0.01 * (level ~/ 5),
parryRate: 0.005 * level,
),
EquipmentSlot.shield => ItemStats(
def: baseValue,
blockRate: 0.02 * (level ~/ 3).clamp(1, 10),
),
def: baseValue,
blockRate: 0.02 * (level ~/ 3).clamp(1, 10),
),
EquipmentSlot.helm => ItemStats(
def: baseValue ~/ 2,
magDef: baseValue ~/ 2,
intBonus: level ~/ 10,
),
def: baseValue ~/ 2,
magDef: baseValue ~/ 2,
intBonus: level ~/ 10,
),
EquipmentSlot.hauberk => ItemStats(def: baseValue, hpBonus: level * 2),
EquipmentSlot.brassairts => ItemStats(
def: baseValue ~/ 2,
strBonus: level ~/ 15,
),
def: baseValue ~/ 2,
strBonus: level ~/ 15,
),
EquipmentSlot.vambraces => ItemStats(
def: baseValue ~/ 2,
dexBonus: level ~/ 15,
),
def: baseValue ~/ 2,
dexBonus: level ~/ 15,
),
EquipmentSlot.gauntlets => ItemStats(
atk: baseValue ~/ 2,
def: baseValue ~/ 4,
),
atk: baseValue ~/ 2,
def: baseValue ~/ 4,
),
EquipmentSlot.gambeson => ItemStats(
def: baseValue ~/ 2,
conBonus: level ~/ 15,
),
def: baseValue ~/ 2,
conBonus: level ~/ 15,
),
EquipmentSlot.cuisses => ItemStats(
def: baseValue ~/ 2,
evasion: 0.005 * level,
),
def: baseValue ~/ 2,
evasion: 0.005 * level,
),
EquipmentSlot.greaves => ItemStats(
def: baseValue ~/ 2,
evasion: 0.003 * level,
),
def: baseValue ~/ 2,
evasion: 0.003 * level,
),
EquipmentSlot.sollerets => ItemStats(
def: baseValue ~/ 3,
evasion: 0.002 * level,
dexBonus: level ~/ 20,
),
def: baseValue ~/ 3,
evasion: 0.002 * level,
dexBonus: level ~/ 20,
),
};
}

View File

@@ -210,7 +210,8 @@ class CombatTickService {
MonsterCombatStats monsterStats,
int totalDamageDealt,
List<CombatEvent> events,
}) _processDotTicks({
})
_processDotTicks({
required List<DotEffect> activeDoTs,
required MonsterCombatStats monsterStats,
required int elapsedMs,
@@ -272,7 +273,8 @@ class CombatTickService {
int lastPotionUsedMs,
PotionInventory potionInventory,
List<CombatEvent> events,
})? _tryEmergencyPotion({
})?
_tryEmergencyPotion({
required CombatStats playerStats,
required PotionInventory potionInventory,
required int lastPotionUsedMs,
@@ -371,7 +373,8 @@ class CombatTickService {
int totalDamageDealt,
List<CombatEvent> events,
bool isFirstPlayerAttack,
}) _processPlayerAttack({
})
_processPlayerAttack({
required GameState state,
required CombatStats playerStats,
required MonsterCombatStats monsterStats,
@@ -508,10 +511,13 @@ class CombatTickService {
newSkillSystem = skillResult.updatedSkillSystem.startGlobalCooldown();
if (skillResult.debuffEffect != null) {
newActiveBuffs = newActiveBuffs
.where((d) => d.effect.id != skillResult.debuffEffect!.effect.id)
.toList()
..add(skillResult.debuffEffect!);
newActiveBuffs =
newActiveBuffs
.where(
(d) => d.effect.id != skillResult.debuffEffect!.effect.id,
)
.toList()
..add(skillResult.debuffEffect!);
}
events.add(
@@ -601,11 +607,8 @@ class CombatTickService {
}
/// 몬스터 공격 처리
({
CombatStats playerStats,
int totalDamageTaken,
List<CombatEvent> events,
}) _processMonsterAttack({
({CombatStats playerStats, int totalDamageTaken, List<CombatEvent> events})
_processMonsterAttack({
required CombatStats playerStats,
required MonsterCombatStats monsterStats,
required List<ActiveBuff> activeDebuffs,

View File

@@ -131,9 +131,7 @@ class IAPService {
final response = await _iap.queryProductDetails(IAPProductIds.all);
if (response.notFoundIDs.isNotEmpty) {
debugPrint(
'[IAPService] Products not found: ${response.notFoundIDs}',
);
debugPrint('[IAPService] Products not found: ${response.notFoundIDs}');
}
for (final product in response.productDetails) {
@@ -238,14 +236,10 @@ class IAPService {
}
// 구매 요청
final purchaseParam = PurchaseParam(
productDetails: _removeAdsProduct!,
);
final purchaseParam = PurchaseParam(productDetails: _removeAdsProduct!);
try {
final success = await _iap.buyNonConsumable(
purchaseParam: purchaseParam,
);
final success = await _iap.buyNonConsumable(purchaseParam: purchaseParam);
debugPrint('[IAPService] Purchase initiated: $success');
return success ? IAPResult.success : IAPResult.failed;
} catch (e) {

View File

@@ -164,8 +164,9 @@ class ItemService {
final magDef = hasMagDef ? (def * 0.7).round() : 0;
// HP 보너스 (Uncommon 이상)
final hpBonus =
rarity.index >= ItemRarity.uncommon.index ? baseValue ~/ 3 : 0;
final hpBonus = rarity.index >= ItemRarity.uncommon.index
? baseValue ~/ 3
: 0;
// CON 보너스 (Rare 이상)
final conBonus = rarity.index >= ItemRarity.rare.index ? rarity.index : 0;
@@ -273,8 +274,7 @@ class ItemService {
EquipmentSlot.greaves => ItemStats(
def: def,
hpBonus: rarity.index >= ItemRarity.rare.index ? baseValue ~/ 3 : 0,
conBonus:
rarity.index >= ItemRarity.rare.index ? rarity.index - 1 : 0,
conBonus: rarity.index >= ItemRarity.rare.index ? rarity.index - 1 : 0,
evasion: rarity.index >= ItemRarity.epic.index ? 0.01 : 0.0,
),

View File

@@ -9,10 +9,7 @@ import 'package:asciineverdie/src/core/util/pq_logic.dart' as pq_logic;
/// 판매 처리 결과
class SellResult {
const SellResult({
required this.state,
required this.continuesSelling,
});
const SellResult({required this.state, required this.continuesSelling});
final GameState state;
final bool continuesSelling;

View File

@@ -193,7 +193,11 @@ class ProgressService {
}
// 5. 시장/판매/구매 태스크 완료 처리
final marketResult = _handleMarketTaskCompletion(nextState, progress, queue);
final marketResult = _handleMarketTaskCompletion(
nextState,
progress,
queue,
);
if (marketResult.earlyReturn != null) return marketResult.earlyReturn!;
nextState = marketResult.state;
progress = marketResult.progress;
@@ -209,7 +213,11 @@ class ProgressService {
// 7. 퀘스트 진행 처리
final questResult = _handleQuestProgress(
nextState, progress, queue, gain, incrementSeconds,
nextState,
progress,
queue,
gain,
incrementSeconds,
);
nextState = questResult.state;
progress = questResult.progress;
@@ -217,9 +225,7 @@ class ProgressService {
questDone = questResult.completed;
// 8. 플롯 진행 및 Act Boss 소환 처리
progress = _handlePlotProgress(
nextState, progress, gain, incrementSeconds,
);
progress = _handlePlotProgress(nextState, progress, gain, incrementSeconds);
// 9. 다음 태스크 디큐/생성
final dequeueResult = _handleTaskDequeue(nextState, progress, queue);
@@ -341,7 +347,8 @@ class ProgressService {
ProgressState progress,
QueueState queue,
ProgressTickResult? earlyReturn,
}) _handleKillTaskCompletion(
})
_handleKillTaskCompletion(
GameState state,
ProgressState progress,
QueueState queue,
@@ -358,8 +365,9 @@ class ProgressService {
final klass = ClassData.findById(nextState.traits.classId);
if (klass != null) {
final postCombatHealRate =
klass.getPassiveValue(ClassPassiveType.postCombatHeal);
final postCombatHealRate = klass.getPassiveValue(
ClassPassiveType.postCombatHeal,
);
if (postCombatHealRate > 0) {
healAmount += (maxHp * postCombatHealRate).round();
}
@@ -446,7 +454,8 @@ class ProgressService {
ProgressState progress,
QueueState queue,
ProgressTickResult? earlyReturn,
}) _handleMarketTaskCompletion(
})
_handleMarketTaskCompletion(
GameState state,
ProgressState progress,
QueueState queue,
@@ -520,12 +529,8 @@ class ProgressService {
}
/// 퀘스트 진행 처리
({
GameState state,
ProgressState progress,
QueueState queue,
bool completed,
}) _handleQuestProgress(
({GameState state, ProgressState progress, QueueState queue, bool completed})
_handleQuestProgress(
GameState state,
ProgressState progress,
QueueState queue,
@@ -603,7 +608,8 @@ class ProgressService {
QueueState queue,
bool actDone,
bool gameComplete,
}) _handleTaskDequeue(
})
_handleTaskDequeue(
GameState state,
ProgressState progress,
QueueState queue,
@@ -705,10 +711,7 @@ class ProgressService {
4 * 1000,
);
final updatedProgress = taskResult.progress.copyWith(
currentTask: TaskInfo(
caption: taskResult.caption,
type: TaskType.market,
),
currentTask: TaskInfo(caption: taskResult.caption, type: TaskType.market),
currentCombat: null,
);
return (progress: updatedProgress, queue: queue);
@@ -1171,8 +1174,10 @@ class ProgressService {
final shouldLoseEquipment = roll < lossChancePercent;
// ignore: avoid_print
print('[Death] Lv$level lossChance=$lossChancePercent% roll=$roll '
'shouldLose=$shouldLoseEquipment');
print(
'[Death] Lv$level lossChance=$lossChancePercent% roll=$roll '
'shouldLose=$shouldLoseEquipment',
);
if (shouldLoseEquipment) {
// 무기(슬롯 0)를 제외한 장착된 장비 중 1개를 제물로 삭제

View File

@@ -346,7 +346,10 @@ class ResurrectionService {
// 해당 슬롯에 아이템 복원
final slotIndex = lostSlot.index;
final updatedEquipment = state.equipment.setItemByIndex(slotIndex, lostItem);
final updatedEquipment = state.equipment.setItemByIndex(
slotIndex,
lostItem,
);
// DeathInfo에서 상실 아이템 정보 제거 (복구 완료)
final updatedDeathInfo = deathInfo.copyWith(

View File

@@ -94,8 +94,10 @@ class ReturnRewardsService {
// 보너스 상자 (광고 시청 시 동일 개수 추가)
final bonusChestCount = chestCount;
debugPrint('[ReturnRewards] $hoursAway hours away, '
'chests=$chestCount, bonus=$bonusChestCount, paid=$isPaidUser');
debugPrint(
'[ReturnRewards] $hoursAway hours away, '
'chests=$chestCount, bonus=$bonusChestCount, paid=$isPaidUser',
);
return ReturnChestReward(
hoursAway: hoursAway,
@@ -125,9 +127,14 @@ class ReturnRewardsService {
/// [reward] 복귀 보상 데이터
/// [playerLevel] 플레이어 레벨
/// Returns: 오픈된 상자 보상 목록
List<ChestReward> claimBasicReward(ReturnChestReward reward, int playerLevel) {
List<ChestReward> claimBasicReward(
ReturnChestReward reward,
int playerLevel,
) {
if (!reward.hasReward) return [];
debugPrint('[ReturnRewards] Basic reward claimed: ${reward.chestCount} chests');
debugPrint(
'[ReturnRewards] Basic reward claimed: ${reward.chestCount} chests',
);
return openChests(reward.chestCount, playerLevel);
}
@@ -146,8 +153,10 @@ class ReturnRewardsService {
// 유료 유저는 무료 보너스
if (IAPService.instance.isAdRemovalPurchased) {
debugPrint('[ReturnRewards] Bonus claimed (paid user): '
'${reward.bonusChestCount} chests');
debugPrint(
'[ReturnRewards] Bonus claimed (paid user): '
'${reward.bonusChestCount} chests',
);
return openChests(reward.bonusChestCount, playerLevel);
}
@@ -161,8 +170,10 @@ class ReturnRewardsService {
);
if (adResult == AdResult.completed || adResult == AdResult.debugSkipped) {
debugPrint('[ReturnRewards] Bonus claimed (free user with ad): '
'${bonusRewards.length} chests');
debugPrint(
'[ReturnRewards] Bonus claimed (free user with ad): '
'${bonusRewards.length} chests',
);
return bonusRewards;
}

View File

@@ -57,46 +57,64 @@ class CombatStats with _$CombatStats {
// 기본 스탯
/// 힘: 물리 공격력 보정
required int str,
/// 체력: HP, 방어력 보정
required int con,
/// 민첩: 회피율, 크리티컬율, 명중률, 공격 속도
required int dex,
/// 지능: 마법 공격력, MP
required int intelligence,
/// 지혜: 마법 방어력, MP 회복
required int wis,
/// 매력: 상점 가격, 드롭률 보정
required int cha,
// 파생 스탯 (전투용)
/// 물리 공격력
required int atk,
/// 물리 방어력
required int def,
/// 마법 공격력
required int magAtk,
/// 마법 방어력
required int magDef,
/// 크리티컬 확률 (0.0 ~ 1.0)
required double criRate,
/// 크리티컬 데미지 배율 (1.5 ~ 3.0)
required double criDamage,
/// 회피율 (0.0 ~ 0.5)
required double evasion,
/// 명중률 (0.8 ~ 1.0)
required double accuracy,
/// 방패 방어율 (0.0 ~ 0.4)
required double blockRate,
/// 무기로 쳐내기 확률 (0.0 ~ 0.3)
required double parryRate,
/// 공격 딜레이 (밀리초)
required int attackDelayMs,
// 자원
/// 최대 HP
required int hpMax,
/// 현재 HP
required int hpCurrent,
/// 최대 MP
required int mpMax,
/// 현재 MP
required int mpCurrent,
}) = _CombatStats;

View File

@@ -41,36 +41,52 @@ class ItemStats with _$ItemStats {
const factory ItemStats({
/// 물리 공격력 보정
@Default(0) int atk,
/// 물리 방어력 보정
@Default(0) int def,
/// 마법 공격력 보정
@Default(0) int magAtk,
/// 마법 방어력 보정
@Default(0) int magDef,
/// 크리티컬 확률 보정 (0.0 ~ 1.0)
@Default(0.0) double criRate,
/// 회피율 보정 (0.0 ~ 1.0)
@Default(0.0) double evasion,
/// 방패 방어율 (방패 전용, 0.0 ~ 1.0)
@Default(0.0) double blockRate,
/// 무기 쳐내기 확률 (무기 전용, 0.0 ~ 1.0)
@Default(0.0) double parryRate,
/// HP 보너스
@Default(0) int hpBonus,
/// MP 보너스
@Default(0) int mpBonus,
/// STR 보너스
@Default(0) int strBonus,
/// CON 보너스
@Default(0) int conBonus,
/// DEX 보너스
@Default(0) int dexBonus,
/// INT 보너스
@Default(0) int intBonus,
/// WIS 보너스
@Default(0) int wisBonus,
/// CHA 보너스
@Default(0) int chaBonus,
/// 무기 공격속도 (밀리초, 무기 전용)
///
/// 0이면 기본값(1000ms) 사용, 값이 클수록 느린 공격.

View File

@@ -121,16 +121,18 @@ class MonetizationState with _$MonetizationState {
List<Map<String, dynamic>>? _statsListToJson(List<Stats>? stats) {
if (stats == null) return null;
return stats
.map((s) => {
'str': s.str,
'con': s.con,
'dex': s.dex,
'int': s.intelligence,
'wis': s.wis,
'cha': s.cha,
'hpMax': s.hpMax,
'mpMax': s.mpMax,
})
.map(
(s) => {
'str': s.str,
'con': s.con,
'dex': s.dex,
'int': s.intelligence,
'wis': s.wis,
'cha': s.cha,
'hpMax': s.hpMax,
'mpMax': s.mpMax,
},
)
.toList();
}

View File

@@ -62,9 +62,7 @@ class Potion {
///
/// 보유 물약 수량 관리 (쿨타임은 CombatState에서 관리)
class PotionInventory {
const PotionInventory({
this.potions = const {},
});
const PotionInventory({this.potions = const {}});
/// 보유 물약 (물약 ID → 수량)
final Map<String, int> potions;
@@ -99,11 +97,7 @@ class PotionInventory {
/// 빈 인벤토리
static const empty = PotionInventory();
PotionInventory copyWith({
Map<String, int>? potions,
}) {
return PotionInventory(
potions: potions ?? this.potions,
);
PotionInventory copyWith({Map<String, int>? potions}) {
return PotionInventory(potions: potions ?? this.potions);
}
}

View File

@@ -28,10 +28,7 @@ class ChestReward {
/// 장비 보상 생성
factory ChestReward.equipment(EquipmentItem item) {
return ChestReward._(
type: ChestRewardType.equipment,
equipment: item,
);
return ChestReward._(type: ChestRewardType.equipment, equipment: item);
}
/// 포션 보상 생성
@@ -45,18 +42,12 @@ class ChestReward {
/// 골드 보상 생성
factory ChestReward.gold(int amount) {
return ChestReward._(
type: ChestRewardType.gold,
gold: amount,
);
return ChestReward._(type: ChestRewardType.gold, gold: amount);
}
/// 경험치 보상 생성
factory ChestReward.experience(int amount) {
return ChestReward._(
type: ChestRewardType.experience,
experience: amount,
);
return ChestReward._(type: ChestRewardType.experience, experience: amount);
}
/// 보상 타입

View File

@@ -714,7 +714,10 @@ class _GamePlayScreenState extends State<GamePlayScreen>
}
/// 데스크톱 앱바
PreferredSizeWidget _buildDesktopAppBar(BuildContext context, GameState state) {
PreferredSizeWidget _buildDesktopAppBar(
BuildContext context,
GameState state,
) {
return AppBar(
backgroundColor: RetroColors.darkBrown,
title: Text(
@@ -969,9 +972,7 @@ class _GamePlayScreenState extends State<GamePlayScreen>
// Potions (물약 인벤토리)
_buildSectionHeader(game_l10n.uiPotions),
Expanded(
child: PotionInventoryPanel(
inventory: state.potionInventory,
),
child: PotionInventoryPanel(inventory: state.potionInventory),
),
// Encumbrance 바

View File

@@ -35,8 +35,8 @@ class GameSessionController extends ChangeNotifier {
DateTime Function()? now,
StatisticsStorage? statisticsStorage,
HallOfFameStorage? hallOfFameStorage,
}) : _tickInterval = tickInterval,
_now = now ?? DateTime.now {
}) : _tickInterval = tickInterval,
_now = now ?? DateTime.now {
// 매니저 초기화
_statisticsManager = GameStatisticsManager(
statisticsStorage: statisticsStorage,
@@ -136,9 +136,9 @@ class GameSessionController extends ChangeNotifier {
int get speedBoostDuration => _speedBoostManager.speedBoostDuration;
int get speedBoostRemainingSeconds => _speedBoostManager.getRemainingSeconds(
_monetization,
_state?.skillSystem.elapsedMs ?? 0,
);
_monetization,
_state?.skillSystem.elapsedMs ?? 0,
);
int get currentSpeedMultiplier =>
_speedBoostManager.getCurrentSpeedMultiplier(_loop);
@@ -472,12 +472,12 @@ class GameSessionController extends ChangeNotifier {
/// 속도 부스트 활성화 (광고 시청 후)
Future<bool> activateSpeedBoost() async {
final (success, updatedMonetization) =
await _speedBoostManager.activateSpeedBoost(
loop: _loop,
monetization: _monetization,
currentElapsedMs: _state?.skillSystem.elapsedMs ?? 0,
);
final (success, updatedMonetization) = await _speedBoostManager
.activateSpeedBoost(
loop: _loop,
monetization: _monetization,
currentElapsedMs: _state?.skillSystem.elapsedMs ?? 0,
);
if (success) {
_monetization = updatedMonetization;

View File

@@ -308,15 +308,14 @@ class _MobileCarouselLayoutState extends State<MobileCarouselLayout> {
// 핸들 바
Padding(
padding: const EdgeInsets.only(top: 8, bottom: 4),
child: Container(
width: 60,
height: 4,
color: border,
),
child: Container(width: 60, height: 4, color: border),
),
// 헤더
Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 8,
),
width: double.infinity,
decoration: BoxDecoration(
color: RetroColors.panelBgOf(context),
@@ -515,7 +514,8 @@ class _MobileCarouselLayoutState extends State<MobileCarouselLayout> {
],
// === 디버그 도구 섹션 ===
if (kDebugMode && widget.onCreateTestCharacter != null) ...[
if (kDebugMode &&
widget.onCreateTestCharacter != null) ...[
const SizedBox(height: 16),
RetroMenuSection(
title: L10n.of(context).debugToolsTitle,
@@ -526,7 +526,9 @@ class _MobileCarouselLayoutState extends State<MobileCarouselLayout> {
icon: Icons.science,
iconColor: RetroColors.warningOf(context),
label: L10n.of(context).debugCreateTestCharacter,
subtitle: L10n.of(context).debugCreateTestCharacterDesc,
subtitle: L10n.of(
context,
).debugCreateTestCharacterDesc,
onTap: () {
Navigator.pop(context);
_showTestCharacterDialog(context);

View File

@@ -9,9 +9,8 @@ import 'package:asciineverdie/src/core/storage/statistics_storage.dart';
/// 세션 통계와 누적 통계를 관리하고, 게임 상태 변화에 따라
/// 통계를 자동 업데이트합니다.
class GameStatisticsManager {
GameStatisticsManager({
StatisticsStorage? statisticsStorage,
}) : _statisticsStorage = statisticsStorage ?? StatisticsStorage();
GameStatisticsManager({StatisticsStorage? statisticsStorage})
: _statisticsStorage = statisticsStorage ?? StatisticsStorage();
final StatisticsStorage _statisticsStorage;

View File

@@ -11,9 +11,8 @@ import 'package:flutter/foundation.dart';
///
/// 게임 클리어 시 캐릭터 등록, 테스트 캐릭터 생성 등을 담당합니다.
class HallOfFameManager {
HallOfFameManager({
HallOfFameStorage? hallOfFameStorage,
}) : _hallOfFameStorage = hallOfFameStorage ?? HallOfFameStorage();
HallOfFameManager({HallOfFameStorage? hallOfFameStorage})
: _hallOfFameStorage = hallOfFameStorage ?? HallOfFameStorage();
final HallOfFameStorage _hallOfFameStorage;

View File

@@ -107,7 +107,8 @@ class ResurrectionManager {
updatedMonetization = monetization.copyWith(autoReviveEndMs: buffEndMs);
debugPrint(
'[Resurrection] Ad revive complete, auto-revive buff until $buffEndMs ms');
'[Resurrection] Ad revive complete, auto-revive buff until $buffEndMs ms',
);
}
// 유료 유저는 광고 없이 부활

View File

@@ -49,8 +49,10 @@ class ReturnRewardsManager {
if (reward.hasReward) {
_pendingReturnReward = reward;
debugPrint('[ReturnRewards] Reward available: ${reward.chestCount} chests, '
'${reward.hoursAway} hours away');
debugPrint(
'[ReturnRewards] Reward available: ${reward.chestCount} chests, '
'${reward.hoursAway} hours away',
);
// UI에서 다이얼로그 표시를 위해 콜백 호출
// startNew 후에 호출하도록 딜레이
@@ -91,11 +93,13 @@ class ReturnRewardsManager {
loop?.replaceState(updatedState); // ProgressLoop 상태도 업데이트
// 저장
unawaited(saveManager.saveState(
updatedState,
cheatsEnabled: cheatsEnabled,
monetization: monetization,
));
unawaited(
saveManager.saveState(
updatedState,
cheatsEnabled: cheatsEnabled,
monetization: monetization,
),
);
_pendingReturnReward = null;
@@ -129,18 +133,19 @@ class ReturnRewardsManager {
reward.equipment!.itemWeight > currentItem.itemWeight) {
debugPrint('[ReturnRewards] Equipped: ${reward.equipment!.name}');
return state.copyWith(
equipment: state.equipment.setItemByIndex(
slotIndex,
reward.equipment!,
),
equipment: state.equipment.setItemByIndex(slotIndex, reward.equipment!),
);
}
// 더 좋지 않으면 판매 (골드로 변환)
final sellPrice =
(reward.equipment!.level * 50 * 0.3).round().clamp(1, 99999);
debugPrint('[ReturnRewards] Sold: ${reward.equipment!.name} '
'for $sellPrice gold');
final sellPrice = (reward.equipment!.level * 50 * 0.3).round().clamp(
1,
99999,
);
debugPrint(
'[ReturnRewards] Sold: ${reward.equipment!.name} '
'for $sellPrice gold',
);
return state.copyWith(
inventory: state.inventory.copyWith(
gold: state.inventory.gold + sellPrice,
@@ -152,8 +157,10 @@ class ReturnRewardsManager {
GameState _applyPotionReward(GameState state, ChestReward reward) {
if (reward.potionId == null) return state;
debugPrint('[ReturnRewards] Added potion: ${reward.potionId} '
'x${reward.potionCount}');
debugPrint(
'[ReturnRewards] Added potion: ${reward.potionId} '
'x${reward.potionCount}',
);
return state.copyWith(
potionInventory: state.potionInventory.addPotion(
reward.potionId!,

View File

@@ -12,8 +12,8 @@ class SpeedBoostManager {
SpeedBoostManager({
required bool Function() cheatsEnabledGetter,
required Future<List<int>> Function() getAvailableSpeeds,
}) : _cheatsEnabledGetter = cheatsEnabledGetter,
_getAvailableSpeeds = getAvailableSpeeds;
}) : _cheatsEnabledGetter = cheatsEnabledGetter,
_getAvailableSpeeds = getAvailableSpeeds;
final bool Function() _cheatsEnabledGetter;
final Future<List<int>> Function() _getAvailableSpeeds;
@@ -52,7 +52,10 @@ class SpeedBoostManager {
int get speedBoostDuration => _speedBoostDuration;
/// 속도 부스트 남은 시간 (초) - 게임 시간(elapsedMs) 기준 계산
int getRemainingSeconds(MonetizationState monetization, int currentElapsedMs) {
int getRemainingSeconds(
MonetizationState monetization,
int currentElapsedMs,
) {
if (!_isSpeedBoostActive) return 0;
final endMs = monetization.speedBoostEndMs;
if (endMs == null) return 0;
@@ -203,7 +206,10 @@ class SpeedBoostManager {
if (_isSpeedBoostActive) {
// 부스트 상태: 부스트 배속만 사용, 기본 배속 저장
savedSpeedMultiplier = baseSpeed;
return (speeds: [speedBoostMultiplier], initialSpeed: speedBoostMultiplier);
return (
speeds: [speedBoostMultiplier],
initialSpeed: speedBoostMultiplier,
);
}
// 일반 상태: 기본 배속 사용
return (speeds: baseAvailableSpeeds, initialSpeed: baseSpeed);

View File

@@ -37,9 +37,7 @@ class InventoryPage extends StatelessWidget {
_buildSectionHeader(context, l10n.uiPotions),
Expanded(
flex: 2,
child: PotionInventoryPanel(
inventory: potionInventory,
),
child: PotionInventoryPanel(inventory: potionInventory),
),
// 무게 (Encumbrance)

View File

@@ -147,7 +147,11 @@ class _SkillTile extends StatelessWidget {
if (isOnCooldown)
const Padding(
padding: EdgeInsets.only(right: 8),
child: Icon(Icons.hourglass_empty, size: 14, color: Colors.orange),
child: Icon(
Icons.hourglass_empty,
size: 14,
color: Colors.orange,
),
),
_RankBadge(rank: rank),
],
@@ -273,10 +277,12 @@ class _SkillStatsGrid extends StatelessWidget {
// 공통: MP, 쿨타임
entries.add(_StatEntry(l10n.skillMpCost, '${skill.mpCost}'));
entries.add(_StatEntry(
l10n.skillCooldown,
'${(skill.cooldownMs / 1000).toStringAsFixed(1)}${l10n.skillSeconds}',
));
entries.add(
_StatEntry(
l10n.skillCooldown,
'${(skill.cooldownMs / 1000).toStringAsFixed(1)}${l10n.skillSeconds}',
),
);
// 타입별 스탯 추가
switch (skill.type) {
@@ -309,33 +315,40 @@ class _SkillStatsGrid extends StatelessWidget {
// DOT 정보
if (skill.isDot && skill.baseDotDamage != null) {
final dotDps = skill.baseDotDamage! *
final dotDps =
skill.baseDotDamage! *
(skill.baseDotDurationMs! / skill.baseDotTickMs!);
entries.add(_StatEntry(l10n.skillDot, '${dotDps.round()}'));
}
// HP 흡수
if (skill.lifestealPercent > 0) {
entries.add(_StatEntry(
l10n.skillLifesteal,
'${(skill.lifestealPercent * 100).round()}%',
));
entries.add(
_StatEntry(
l10n.skillLifesteal,
'${(skill.lifestealPercent * 100).round()}%',
),
);
}
// 방어 무시
if (skill.targetDefReduction > 0) {
entries.add(_StatEntry(
l10n.skillDefPen,
'${(skill.targetDefReduction * 100).round()}%',
));
entries.add(
_StatEntry(
l10n.skillDefPen,
'${(skill.targetDefReduction * 100).round()}%',
),
);
}
// 자해 데미지
if (skill.selfDamagePercent > 0) {
entries.add(_StatEntry(
l10n.skillSelfDmg,
'${(skill.selfDamagePercent * 100).round()}%',
));
entries.add(
_StatEntry(
l10n.skillSelfDmg,
'${(skill.selfDamagePercent * 100).round()}%',
),
);
}
}
@@ -347,10 +360,12 @@ class _SkillStatsGrid extends StatelessWidget {
// % 회복
if (skill.healPercent > 0) {
entries.add(_StatEntry(
l10n.skillHealPercent,
'${(skill.healPercent * 100).round()}%',
));
entries.add(
_StatEntry(
l10n.skillHealPercent,
'${(skill.healPercent * 100).round()}%',
),
);
}
// MP 회복
@@ -361,10 +376,12 @@ class _SkillStatsGrid extends StatelessWidget {
// 부가 버프
if (skill.buff != null) {
final buff = skill.buff!;
entries.add(_StatEntry(
l10n.skillBuffDuration,
'${(buff.durationMs / 1000).round()}${l10n.skillSeconds}',
));
entries.add(
_StatEntry(
l10n.skillBuffDuration,
'${(buff.durationMs / 1000).round()}${l10n.skillSeconds}',
),
);
}
}
@@ -373,39 +390,49 @@ class _SkillStatsGrid extends StatelessWidget {
final buff = skill.buff!;
// 지속시간
entries.add(_StatEntry(
l10n.skillBuffDuration,
'${(buff.durationMs / 1000).round()}${l10n.skillSeconds}',
));
entries.add(
_StatEntry(
l10n.skillBuffDuration,
'${(buff.durationMs / 1000).round()}${l10n.skillSeconds}',
),
);
// 각 보정치
if (buff.atkModifier != 0) {
final sign = buff.atkModifier > 0 ? '+' : '';
entries.add(_StatEntry(
l10n.skillAtkMod,
'$sign${(buff.atkModifier * 100).round()}%',
));
entries.add(
_StatEntry(
l10n.skillAtkMod,
'$sign${(buff.atkModifier * 100).round()}%',
),
);
}
if (buff.defModifier != 0) {
final sign = buff.defModifier > 0 ? '+' : '';
entries.add(_StatEntry(
l10n.skillDefMod,
'$sign${(buff.defModifier * 100).round()}%',
));
entries.add(
_StatEntry(
l10n.skillDefMod,
'$sign${(buff.defModifier * 100).round()}%',
),
);
}
if (buff.criRateModifier != 0) {
final sign = buff.criRateModifier > 0 ? '+' : '';
entries.add(_StatEntry(
l10n.skillCriMod,
'$sign${(buff.criRateModifier * 100).round()}%',
));
entries.add(
_StatEntry(
l10n.skillCriMod,
'$sign${(buff.criRateModifier * 100).round()}%',
),
);
}
if (buff.evasionModifier != 0) {
final sign = buff.evasionModifier > 0 ? '+' : '';
entries.add(_StatEntry(
l10n.skillEvaMod,
'$sign${(buff.evasionModifier * 100).round()}%',
));
entries.add(
_StatEntry(
l10n.skillEvaMod,
'$sign${(buff.evasionModifier * 100).round()}%',
),
);
}
}
@@ -414,23 +441,23 @@ class _SkillStatsGrid extends StatelessWidget {
final buff = skill.buff!;
// 지속시간
entries.add(_StatEntry(
l10n.skillBuffDuration,
'${(buff.durationMs / 1000).round()}${l10n.skillSeconds}',
));
entries.add(
_StatEntry(
l10n.skillBuffDuration,
'${(buff.durationMs / 1000).round()}${l10n.skillSeconds}',
),
);
// 디버프 효과 (보통 음수)
if (buff.atkModifier != 0) {
entries.add(_StatEntry(
l10n.skillAtkMod,
'${(buff.atkModifier * 100).round()}%',
));
entries.add(
_StatEntry(l10n.skillAtkMod, '${(buff.atkModifier * 100).round()}%'),
);
}
if (buff.defModifier != 0) {
entries.add(_StatEntry(
l10n.skillDefMod,
'${(buff.defModifier * 100).round()}%',
));
entries.add(
_StatEntry(l10n.skillDefMod, '${(buff.defModifier * 100).round()}%'),
);
}
}
}

View File

@@ -333,7 +333,8 @@ class DeathOverlay extends StatelessWidget {
TextSpan(
children: [
TextSpan(
text: '[${_getSlotName(deathInfo.lostItemSlot)}] ',
text:
'[${_getSlotName(deathInfo.lostItemSlot)}] ',
style: TextStyle(color: muted),
),
TextSpan(
@@ -485,7 +486,10 @@ class DeathOverlay extends StatelessWidget {
border: Border(
top: BorderSide(color: gold, width: 3),
left: BorderSide(color: gold, width: 3),
bottom: BorderSide(color: goldDark.withValues(alpha: 0.8), width: 3),
bottom: BorderSide(
color: goldDark.withValues(alpha: 0.8),
width: 3,
),
right: BorderSide(color: goldDark.withValues(alpha: 0.8), width: 3),
),
),
@@ -495,10 +499,7 @@ class DeathOverlay extends StatelessWidget {
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'',
style: TextStyle(fontSize: 20, color: gold),
),
Text('', style: TextStyle(fontSize: 20, color: gold)),
const SizedBox(width: 8),
Text(
l10n.deathAdRevive.toUpperCase(),
@@ -551,7 +552,8 @@ class DeathOverlay extends StatelessWidget {
_buildBenefitRow(
context,
icon: '🔄',
text: '${l10n.deathAdReviveItem}: ${deathInfo.lostItemName}',
text:
'${l10n.deathAdReviveItem}: ${deathInfo.lostItemName}',
color: itemRarityColor,
),
const SizedBox(height: 4),

View File

@@ -83,7 +83,9 @@ class RetroOptionItem extends StatelessWidget {
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
decoration: BoxDecoration(
color: isSelected ? gold.withValues(alpha: 0.15) : Colors.transparent,
color: isSelected
? gold.withValues(alpha: 0.15)
: Colors.transparent,
border: Border.all(
color: isSelected ? gold : border,
width: isSelected ? 2 : 1,
@@ -101,7 +103,9 @@ class RetroOptionItem extends StatelessWidget {
style: TextStyle(
fontFamily: 'PressStart2P',
fontSize: 18,
color: isSelected ? gold : RetroColors.textPrimaryOf(context),
color: isSelected
? gold
: RetroColors.textPrimaryOf(context),
),
),
),

View File

@@ -300,7 +300,9 @@ class _EnhancedAnimationPanelState extends State<EnhancedAnimationPanel>
child: _buildBuffChip(
icon: '',
label: '${widget.adSpeedMultiplier}x',
remainingMs: widget.isPaidUser ? -1 : _speedBoostRemainingMs,
remainingMs: widget.isPaidUser
? -1
: _speedBoostRemainingMs,
color: Colors.orange,
isPermanent: widget.isPaidUser,
),
@@ -401,7 +403,9 @@ class _EnhancedAnimationPanelState extends State<EnhancedAnimationPanel>
child: SizedBox.expand(
child: LinearProgressIndicator(
value: ratio.clamp(0.0, 1.0),
backgroundColor: Colors.red.withValues(alpha: 0.2),
backgroundColor: Colors.red.withValues(
alpha: 0.2,
),
valueColor: AlwaysStoppedAnimation(
isLow ? Colors.red : Colors.red.shade600,
),
@@ -502,7 +506,9 @@ class _EnhancedAnimationPanelState extends State<EnhancedAnimationPanel>
child: SizedBox.expand(
child: LinearProgressIndicator(
value: ratio.clamp(0.0, 1.0),
backgroundColor: Colors.blue.withValues(alpha: 0.2),
backgroundColor: Colors.blue.withValues(
alpha: 0.2,
),
valueColor: AlwaysStoppedAnimation(
Colors.blue.shade600,
),
@@ -619,7 +625,10 @@ class _EnhancedAnimationPanelState extends State<EnhancedAnimationPanel>
color: Colors.black.withValues(alpha: 0.8),
blurRadius: 2,
),
const Shadow(color: Colors.black, blurRadius: 4),
const Shadow(
color: Colors.black,
blurRadius: 4,
),
],
),
),
@@ -783,8 +792,9 @@ class _EnhancedAnimationPanelState extends State<EnhancedAnimationPanel>
),
TextSpan(
text: _getStatusMessage(),
style:
gradeColor != null ? TextStyle(color: gradeColor) : null,
style: gradeColor != null
? TextStyle(color: gradeColor)
: null,
),
],
),
@@ -844,10 +854,7 @@ class _EnhancedAnimationPanelState extends State<EnhancedAnimationPanel>
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
icon,
style: TextStyle(fontSize: 12, color: color),
),
Text(icon, style: TextStyle(fontSize: 12, color: color)),
if (label != null) ...[
const SizedBox(width: 2),
Text(

View File

@@ -326,8 +326,10 @@ class _HpMpBarState extends State<HpMpBar> with TickerProviderStateMixin {
height: 14,
decoration: BoxDecoration(
color: emptyColor.withValues(alpha: 0.3),
border:
Border.all(color: RetroColors.panelBorderOuter, width: 1),
border: Border.all(
color: RetroColors.panelBorderOuter,
width: 1,
),
),
child: Row(
children: List.generate(segmentCount, (index) {
@@ -341,10 +343,8 @@ class _HpMpBarState extends State<HpMpBar> with TickerProviderStateMixin {
border: Border(
right: index < segmentCount - 1
? BorderSide(
color:
RetroColors.panelBorderOuter.withValues(
alpha: 0.3,
),
color: RetroColors.panelBorderOuter
.withValues(alpha: 0.3),
width: 1,
)
: BorderSide.none,

View File

@@ -4,11 +4,7 @@ import 'package:asciineverdie/src/shared/retro_colors.dart';
/// 메뉴 섹션 타이틀
class RetroMenuSection extends StatelessWidget {
const RetroMenuSection({
super.key,
required this.title,
this.color,
});
const RetroMenuSection({super.key, required this.title, this.color});
final String title;
final Color? color;
@@ -182,10 +178,7 @@ class RetroSpeedChip extends StatelessWidget {
if (isAdBased && !isSelected && !isDisabled)
Padding(
padding: const EdgeInsets.only(right: 2),
child: Text(
'',
style: TextStyle(fontSize: 7, color: warning),
),
child: Text('', style: TextStyle(fontSize: 7, color: warning)),
),
Text(
'${speed}x',

View File

@@ -9,10 +9,7 @@ import 'package:asciineverdie/src/core/model/potion.dart';
/// 보유 중인 물약 목록과 수량을 표시.
/// HP 물약은 빨간색, MP 물약은 파란색으로 구분.
class PotionInventoryPanel extends StatelessWidget {
const PotionInventoryPanel({
super.key,
required this.inventory,
});
const PotionInventoryPanel({super.key, required this.inventory});
final PotionInventory inventory;
@@ -38,10 +35,7 @@ class PotionInventoryPanel extends StatelessWidget {
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
itemBuilder: (context, index) {
final entry = potionEntries[index];
return _PotionRow(
potion: entry.potion,
quantity: entry.quantity,
);
return _PotionRow(potion: entry.potion, quantity: entry.quantity);
},
);
}
@@ -82,10 +76,7 @@ class _PotionEntry {
/// 물약 행 위젯
class _PotionRow extends StatelessWidget {
const _PotionRow({
required this.potion,
required this.quantity,
});
const _PotionRow({required this.potion, required this.quantity});
final Potion potion;
final int quantity;

View File

@@ -264,7 +264,8 @@ class _ReturnRewardsDialogState extends State<ReturnRewardsDialog>
return Transform.translate(
offset: isOpening
? Offset(
_shakeAnimation.value * 2 *
_shakeAnimation.value *
2 *
((_animController.value * 10).round() % 2 == 0
? 1
: -1),
@@ -314,8 +315,8 @@ class _ReturnRewardsDialogState extends State<ReturnRewardsDialog>
isGold
? l10n.returnRewardOpenChests
: (isPaidUser
? l10n.returnRewardClaimBonusFree
: l10n.returnRewardClaimBonus),
? l10n.returnRewardClaimBonusFree
: l10n.returnRewardClaimBonus),
style: TextStyle(
fontFamily: 'PressStart2P',
fontSize: 10,
@@ -365,10 +366,7 @@ class _ReturnRewardsDialogState extends State<ReturnRewardsDialog>
count,
(index) => Text(
'📦',
style: TextStyle(
fontSize: 24,
color: enabled ? null : muted,
),
style: TextStyle(fontSize: 24, color: enabled ? null : muted),
),
),
);
@@ -387,7 +385,9 @@ class _ReturnRewardsDialogState extends State<ReturnRewardsDialog>
}
return Column(
children: rewards.map((reward) => _buildRewardItem(context, reward)).toList(),
children: rewards
.map((reward) => _buildRewardItem(context, reward))
.toList(),
);
}

View File

@@ -65,10 +65,7 @@ class SpeedBoostButton extends StatelessWidget {
Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'',
style: TextStyle(fontSize: 18, color: expColor),
),
Text('', style: TextStyle(fontSize: 18, color: expColor)),
const SizedBox(width: 4),
Text(
'${boostMultiplier}x',
@@ -113,10 +110,7 @@ class SpeedBoostButton extends StatelessWidget {
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'',
style: TextStyle(fontSize: 18, color: gold),
),
Text('', style: TextStyle(fontSize: 18, color: gold)),
const SizedBox(width: 4),
Text(
'${boostMultiplier}x',
@@ -130,10 +124,7 @@ class SpeedBoostButton extends StatelessWidget {
if (!isPaidUser) ...[
const SizedBox(width: 6),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 4,
vertical: 2,
),
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2),
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(4),

View File

@@ -257,7 +257,11 @@ class _NewCharacterScreenState extends State<NewCharacterScreen> {
mainAxisSize: MainAxisSize.min,
children: [
if (!isPaidUser) ...[
const Icon(Icons.play_circle, size: 14, color: RetroColors.gold),
const Icon(
Icons.play_circle,
size: 14,
color: RetroColors.gold,
),
const SizedBox(width: 4),
],
Text(

View File

@@ -105,8 +105,9 @@ class _ClassInfo extends StatelessWidget {
final percent = (passive.value * 100).round();
return switch (passive.type) {
ClassPassiveType.hpBonus => game_l10n.passiveHpBonus(percent),
ClassPassiveType.physicalDamageBonus =>
game_l10n.passivePhysicalBonus(percent),
ClassPassiveType.physicalDamageBonus => game_l10n.passivePhysicalBonus(
percent,
),
ClassPassiveType.defenseBonus => game_l10n.passiveDefenseBonus(percent),
ClassPassiveType.magicDamageBonus => game_l10n.passiveMagicBonus(percent),
ClassPassiveType.evasionBonus => game_l10n.passiveEvasionBonus(percent),

View File

@@ -62,9 +62,15 @@ class StatsSection extends StatelessWidget {
// 스탯 그리드
Row(
children: [
Expanded(child: _StatTile(label: l10n.statStr, value: str)),
Expanded(child: _StatTile(label: l10n.statCon, value: con)),
Expanded(child: _StatTile(label: l10n.statDex, value: dex)),
Expanded(
child: _StatTile(label: l10n.statStr, value: str),
),
Expanded(
child: _StatTile(label: l10n.statCon, value: con),
),
Expanded(
child: _StatTile(label: l10n.statDex, value: dex),
),
],
),
const SizedBox(height: 8),
@@ -73,8 +79,12 @@ class StatsSection extends StatelessWidget {
Expanded(
child: _StatTile(label: l10n.statInt, value: intelligence),
),
Expanded(child: _StatTile(label: l10n.statWis, value: wis)),
Expanded(child: _StatTile(label: l10n.statCha, value: cha)),
Expanded(
child: _StatTile(label: l10n.statWis, value: wis),
),
Expanded(
child: _StatTile(label: l10n.statCha, value: cha),
),
],
),
const SizedBox(height: 12),
@@ -116,10 +126,7 @@ class StatsSection extends StatelessWidget {
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_UndoButton(
canUndo: canUndo,
onPressed: onUndo,
),
_UndoButton(canUndo: canUndo, onPressed: onUndo),
const SizedBox(width: 16),
_RollButton(
canRoll: canRoll,
@@ -222,11 +229,7 @@ class _UndoButton extends StatelessWidget {
children: [
// 무료 유저는 광고 아이콘 표시
if (!isPaidUser && canUndo) ...[
const Icon(
Icons.play_circle,
size: 14,
color: RetroColors.gold,
),
const Icon(Icons.play_circle, size: 14, color: RetroColors.gold),
const SizedBox(width: 4),
],
Icon(
@@ -240,7 +243,9 @@ class _UndoButton extends StatelessWidget {
style: TextStyle(
fontFamily: 'PressStart2P',
fontSize: 11,
color: canUndo ? RetroColors.textLight : RetroColors.textDisabled,
color: canUndo
? RetroColors.textLight
: RetroColors.textDisabled,
),
),
],