- refactoring conifg
This commit is contained in:
Horoli 2025-12-07 14:56:43 +09:00
parent d3fca333cb
commit 8771f2c1af
15 changed files with 295 additions and 156 deletions

View File

@ -0,0 +1,30 @@
class GameConfig {
// Inventory
static const int maxInventorySize = 5;
// Economy
static const int startingGold = 50;
static const double sellPriceMultiplier = 0.6;
static const int shopRerollCost = 50;
// Stages
static const int eliteStageInterval = 10;
static const int shopStageInterval = 5;
static const int restStageInterval = 8;
static const int tier1StageMax = 12;
static const int tier2StageMax = 24;
// Battle
static const double stageHealRatio = 0.1;
static const double vulnerableDamageMultiplier = 1.5;
static const double armorDecayRate = 0.5;
// Animations (Duration in milliseconds)
static const int animDelaySafe = 500;
static const int animDelayNormal = 400;
static const int animDelayRisky = 1100;
static const int animDelayEnemyTurn = 1000;
// Save System
static const String saveKey = 'game_save_data';
}

View File

@ -1,6 +1,43 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class ThemeConfig { class ThemeConfig {
// Font Sizes
static const double fontSizeTiny = 8.0;
static const double fontSizeSmall = 10.0;
static const double fontSizeMedium = 12.0;
static const double fontSizeBody = 14.0;
static const double fontSizeLarge = 16.0;
static const double fontSizeHeader = 18.0;
static const double fontSizeXLarge = 20.0;
static const double fontSizeTitle = 24.0;
static const double fontSizeHero = 32.0;
static const double fontSizeHuge = 48.0;
// Font Weights
static const FontWeight fontWeightNormal = FontWeight.normal;
static const FontWeight fontWeightBold = FontWeight.bold;
// Main Menu Colors
static const Color mainMenuBgTop = Colors.black;
static final Color mainMenuBgBottom = Colors.blueGrey[900]!;
static const Color mainTitleColor = Colors.white;
static const Color subTitleColor = Colors.grey;
static const Color mainIconColor = Colors.amber;
// Button Colors
static const Color btnNewGameBg = Color(0xFFFFA000); // Colors.amber[700]
static const Color btnNewGameText = Colors.black;
static const Color btnContinueBg = Colors.blueAccent;
static const Color btnContinueText = Colors.white;
static const Color btnRestBg = Colors.blue;
static const Color btnLeaveBg = Colors.redAccent;
static const Color btnRerollBg = Colors.blueGrey;
static const Color btnActionActive = Colors.redAccent; // Attack
static const Color btnDefendActive = Colors.blueAccent; // Defend
static const Color btnDisabled = Colors.grey;
static const Color btnRestartBg = Colors.orange;
static const Color btnReturnMenuBg = Colors.red;
// Stat Colors // Stat Colors
static const Color statHpColor = Colors.red; static const Color statHpColor = Colors.red;
static const Color statHpPlayerColor = Colors.green; static const Color statHpPlayerColor = Colors.green;
@ -22,6 +59,13 @@ class ThemeConfig {
0xFF546E7A, 0xFF546E7A,
); // Colors.blueGrey[600] ); // Colors.blueGrey[600]
static const Color emptySlotBg = Color(0xFF424242); // Colors.grey[800] static const Color emptySlotBg = Color(0xFF424242); // Colors.grey[800]
static const Color battleBg = Colors.black87;
static const Color shopBg = Colors.black87;
static final Color? shopItemCardBg = Colors.blueGrey[800];
static const Color enemyIntentBg = Colors.black54;
static const Color enemyIntentBorder = Colors.redAccent;
static final Color? selectionCardBg = Colors.blueGrey[800];
static const Color selectionIconColor = Colors.blue;
// Feedback Colors // Feedback Colors
static const Color damageTextDefault = Colors.red; static const Color damageTextDefault = Colors.red;
@ -29,6 +73,9 @@ class ThemeConfig {
static const Color missText = Colors.grey; static const Color missText = Colors.grey;
static const Color failedText = Colors.redAccent; static const Color failedText = Colors.redAccent;
static const Color feedbackShadow = Colors.black; static const Color feedbackShadow = Colors.black;
static const Color statDiffPositive = Colors.green;
static const Color statDiffNegative = Colors.red;
static const Color statDiffNeutral = Colors.grey;
// Status Effect Colors // Status Effect Colors
static const Color effectBg = Colors.deepOrange; static const Color effectBg = Colors.deepOrange;
@ -39,4 +86,10 @@ class ThemeConfig {
static const Color rarityRare = Colors.yellow; static const Color rarityRare = Colors.yellow;
static const Color rarityLegendary = Colors.orange; static const Color rarityLegendary = Colors.orange;
static const Color rarityUnique = Colors.purple; static const Color rarityUnique = Colors.purple;
static const Color rarityCommon = Colors.grey;
// Risk Colors (from BattleScreen Dialog)
static const Color riskSafe = Colors.green;
static const Color riskNormal = Colors.blue;
static const Color riskRisky = Colors.red;
} }

View File

@ -3,6 +3,7 @@ import 'status_effect.dart';
import 'stat_modifier.dart'; import 'stat_modifier.dart';
import '../enums.dart'; import '../enums.dart';
import '../data/item_table.dart'; import '../data/item_table.dart';
import '../config/game_config.dart';
class Character { class Character {
String name; String name;
@ -16,7 +17,7 @@ class Character {
String? image; // New: Image path String? image; // New: Image path
Map<EquipmentSlot, Item> equipment = {}; Map<EquipmentSlot, Item> equipment = {};
List<Item> inventory = []; List<Item> inventory = [];
final int maxInventorySize = 16; final int maxInventorySize = GameConfig.maxInventorySize;
// Active status effects // Active status effects
List<StatusEffect> statusEffects = []; List<StatusEffect> statusEffects = [];

View File

@ -2,9 +2,10 @@ import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import '../providers/battle_provider.dart'; import '../providers/battle_provider.dart';
import 'model/entity.dart'; import 'model/entity.dart';
import 'config/game_config.dart';
class SaveManager { class SaveManager {
static const String _saveKey = 'game_save_data'; static const String _saveKey = GameConfig.saveKey;
static Future<void> saveGame(BattleProvider provider) async { static Future<void> saveGame(BattleProvider provider) async {
final prefs = await SharedPreferences.getInstance(); final prefs = await SharedPreferences.getInstance();

View File

@ -16,6 +16,7 @@ import '../game/model/damage_event.dart'; // DamageEvent import
import '../game/model/effect_event.dart'; // EffectEvent import import '../game/model/effect_event.dart'; // EffectEvent import
import '../game/save_manager.dart'; import '../game/save_manager.dart';
import '../game/config/game_config.dart';
class EnemyIntent { class EnemyIntent {
final EnemyActionType type; final EnemyActionType type;
@ -104,7 +105,7 @@ class BattleProvider with ChangeNotifier {
} }
// Give test gold // Give test gold
player.gold = 50; player.gold = GameConfig.startingGold;
// Provide starter equipment // Provide starter equipment
final starterSword = Item( final starterSword = Item(
@ -172,11 +173,11 @@ class BattleProvider with ChangeNotifier {
StageType type; StageType type;
// Stage Type Logic // Stage Type Logic
if (stage % 10 == 0) { if (stage % GameConfig.eliteStageInterval == 0) {
type = StageType.elite; // Every 10th stage is a Boss/Elite type = StageType.elite; // Every 10th stage is a Boss/Elite
} else if (stage % 5 == 0) { } else if (stage % GameConfig.shopStageInterval == 0) {
type = StageType.shop; // Every 5th stage is a Shop (except 10, 20...) type = StageType.shop; // Every 5th stage is a Shop (except 10, 20...)
} else if (stage % 8 == 0) { } else if (stage % GameConfig.restStageInterval == 0) {
type = StageType.rest; // Every 8th stage is a Rest type = StageType.rest; // Every 8th stage is a Rest
} else { } else {
type = StageType.battle; type = StageType.battle;
@ -255,9 +256,9 @@ class BattleProvider with ChangeNotifier {
/// Generate 4 random items for the shop based on current stage tier /// Generate 4 random items for the shop based on current stage tier
List<Item> _generateShopItems() { List<Item> _generateShopItems() {
ItemTier currentTier = ItemTier.tier1; ItemTier currentTier = ItemTier.tier1;
if (stage > 24) if (stage > GameConfig.tier2StageMax)
currentTier = ItemTier.tier3; currentTier = ItemTier.tier3;
else if (stage > 12) else if (stage > GameConfig.tier1StageMax)
currentTier = ItemTier.tier2; currentTier = ItemTier.tier2;
List<Item> items = []; List<Item> items = [];
@ -271,7 +272,7 @@ class BattleProvider with ChangeNotifier {
} }
void rerollShopItems() { void rerollShopItems() {
const int rerollCost = 50; const int rerollCost = GameConfig.shopRerollCost;
if (player.gold >= rerollCost) { if (player.gold >= rerollCost) {
player.gold -= rerollCost; player.gold -= rerollCost;
// Modify the existing list because shopItems is final // Modify the existing list because shopItems is final
@ -381,11 +382,11 @@ class BattleProvider with ChangeNotifier {
// Animation Delays to sync with Impact // Animation Delays to sync with Impact
if (risk == RiskLevel.safe) { if (risk == RiskLevel.safe) {
await Future.delayed(const Duration(milliseconds: 500)); await Future.delayed(const Duration(milliseconds: GameConfig.animDelaySafe));
} else if (risk == RiskLevel.normal) { } else if (risk == RiskLevel.normal) {
await Future.delayed(const Duration(milliseconds: 400)); await Future.delayed(const Duration(milliseconds: GameConfig.animDelayNormal));
} else if (risk == RiskLevel.risky) { } else if (risk == RiskLevel.risky) {
await Future.delayed(const Duration(milliseconds: 1100)); await Future.delayed(const Duration(milliseconds: GameConfig.animDelayRisky));
} }
int damageToHp = 0; int damageToHp = 0;
@ -477,19 +478,19 @@ class BattleProvider with ChangeNotifier {
return; return;
} }
Future.delayed(const Duration(seconds: 1), () => _enemyTurn()); Future.delayed(const Duration(milliseconds: GameConfig.animDelayEnemyTurn), () => _enemyTurn());
} }
Future<void> _enemyTurn() async { Future<void> _enemyTurn() async {
if (!isPlayerTurn && (player.isDead || enemy.isDead)) return; if (!isPlayerTurn && (player.isDead || enemy.isDead)) return;
_addLog("Enemy's turn..."); _addLog("Enemy's turn...");
await Future.delayed(const Duration(seconds: 1)); await Future.delayed(const Duration(milliseconds: GameConfig.animDelayEnemyTurn));
// Enemy Turn Start Logic // Enemy Turn Start Logic
// Armor decay // Armor decay
if (enemy.armor > 0) { if (enemy.armor > 0) {
enemy.armor = (enemy.armor * 0.5).toInt(); enemy.armor = (enemy.armor * GameConfig.armorDecayRate).toInt();
_addLog("Enemy's armor decayed to ${enemy.armor}."); _addLog("Enemy's armor decayed to ${enemy.armor}.");
} }
@ -578,7 +579,7 @@ class BattleProvider with ChangeNotifier {
// Player Turn Start Logic // Player Turn Start Logic
// Armor decay // Armor decay
if (player.armor > 0) { if (player.armor > 0) {
player.armor = (player.armor * 0.5).toInt(); player.armor = (player.armor * GameConfig.armorDecayRate).toInt();
_addLog("Player's armor decayed to ${player.armor}."); _addLog("Player's armor decayed to ${player.armor}.");
} }
@ -665,7 +666,7 @@ class BattleProvider with ChangeNotifier {
}) { }) {
// Check Vulnerable // Check Vulnerable
if (target.hasStatus(StatusEffectType.vulnerable)) { if (target.hasStatus(StatusEffectType.vulnerable)) {
damage = (damage * 1.5).toInt(); damage = (damage * GameConfig.vulnerableDamageMultiplier).toInt();
_addLog("Vulnerable! Damage increased to $damage."); _addLog("Vulnerable! Damage increased to $damage.");
type = DamageType.vulnerable; type = DamageType.vulnerable;
} }
@ -704,9 +705,9 @@ class BattleProvider with ChangeNotifier {
// Since we just refactored ItemTable, let's use getRandomItem! // Since we just refactored ItemTable, let's use getRandomItem!
ItemTier currentTier = ItemTier.tier1; ItemTier currentTier = ItemTier.tier1;
if (stage > 24) if (stage > GameConfig.tier2StageMax)
currentTier = ItemTier.tier3; currentTier = ItemTier.tier3;
else if (stage > 12) else if (stage > GameConfig.tier1StageMax)
currentTier = ItemTier.tier2; currentTier = ItemTier.tier2;
rewardOptions = []; rewardOptions = [];
@ -749,7 +750,7 @@ class BattleProvider with ChangeNotifier {
} }
// Heal player after selecting reward // Heal player after selecting reward
int healAmount = GameMath.floor(player.totalMaxHp * 0.1); int healAmount = GameMath.floor(player.totalMaxHp * GameConfig.stageHealRatio);
player.heal(healAmount); player.heal(healAmount);
_addLog("Stage Cleared! Recovered $healAmount HP."); _addLog("Stage Cleared! Recovered $healAmount HP.");
@ -793,7 +794,7 @@ class BattleProvider with ChangeNotifier {
void sellItem(Item item) { void sellItem(Item item) {
if (player.inventory.remove(item)) { if (player.inventory.remove(item)) {
int sellPrice = GameMath.floor(item.price * 0.6); int sellPrice = GameMath.floor(item.price * GameConfig.sellPriceMultiplier);
player.gold += sellPrice; player.gold += sellPrice;
_addLog("Sold ${item.name} for $sellPrice G."); _addLog("Sold ${item.name} for $sellPrice G.");
notifyListeners(); notifyListeners();

View File

@ -19,6 +19,7 @@ import '../widgets/battle/battle_animation_widget.dart';
import '../widgets/battle/explosion_widget.dart'; import '../widgets/battle/explosion_widget.dart';
import 'main_menu_screen.dart'; import 'main_menu_screen.dart';
import '../game/config/battle_config.dart'; import '../game/config/battle_config.dart';
import '../game/config/theme_config.dart';
class BattleScreen extends StatefulWidget { class BattleScreen extends StatefulWidget {
const BattleScreen({super.key}); const BattleScreen({super.key});
@ -162,15 +163,15 @@ class _BattleScreenState extends State<BattleScreen> {
switch (event.feedbackType) { switch (event.feedbackType) {
case BattleFeedbackType.miss: case BattleFeedbackType.miss:
feedbackText = "MISS"; feedbackText = "MISS";
feedbackColor = Colors.grey; feedbackColor = ThemeConfig.missText;
break; break;
case BattleFeedbackType.failed: case BattleFeedbackType.failed:
feedbackText = "FAILED"; feedbackText = "FAILED";
feedbackColor = Colors.redAccent; feedbackColor = ThemeConfig.failedText;
break; break;
default: default:
feedbackText = ""; // Should not happen with current enums feedbackText = ""; // Should not happen with current enums
feedbackColor = Colors.white; feedbackColor = ThemeConfig.textColorWhite;
} }
final String id = UniqueKey().toString(); final String id = UniqueKey().toString();
@ -311,15 +312,15 @@ class _BattleScreenState extends State<BattleScreen> {
switch (risk) { switch (risk) {
case RiskLevel.safe: case RiskLevel.safe:
efficiency = 0.5; efficiency = 0.5;
infoColor = Colors.green; infoColor = ThemeConfig.riskSafe;
break; break;
case RiskLevel.normal: case RiskLevel.normal:
efficiency = 1.0; efficiency = 1.0;
infoColor = Colors.blue; infoColor = ThemeConfig.riskNormal;
break; break;
case RiskLevel.risky: case RiskLevel.risky:
efficiency = 2.0; efficiency = 2.0;
infoColor = Colors.red; infoColor = ThemeConfig.riskRisky;
break; break;
} }
@ -391,7 +392,7 @@ class _BattleScreenState extends State<BattleScreen> {
key: _stackKey, key: _stackKey,
children: [ children: [
// 1. Background (Black) // 1. Background (Black)
Container(color: Colors.black87), Container(color: ThemeConfig.battleBg),
// 2. Battle Content (Top Bar + Characters) // 2. Battle Content (Top Bar + Characters)
Column( Column(
@ -408,9 +409,9 @@ class _BattleScreenState extends State<BattleScreen> {
child: Text( child: Text(
"Stage ${battleProvider.stage}", "Stage ${battleProvider.stage}",
style: const TextStyle( style: const TextStyle(
color: Colors.white, color: ThemeConfig.textColorWhite,
fontSize: 18, fontSize: ThemeConfig.fontSizeHeader,
fontWeight: FontWeight.bold, fontWeight: ThemeConfig.fontWeightBold,
), ),
), ),
), ),
@ -421,8 +422,8 @@ class _BattleScreenState extends State<BattleScreen> {
child: Text( child: Text(
"Turn ${battleProvider.turnCount}", "Turn ${battleProvider.turnCount}",
style: const TextStyle( style: const TextStyle(
color: Colors.white, color: ThemeConfig.textColorWhite,
fontSize: 18, fontSize: ThemeConfig.fontSizeHeader,
), ),
), ),
), ),
@ -489,7 +490,7 @@ class _BattleScreenState extends State<BattleScreen> {
context, context,
"ATK", "ATK",
Icons.whatshot, Icons.whatshot,
Colors.redAccent, ThemeConfig.btnActionActive,
ActionType.attack, ActionType.attack,
battleProvider.isPlayerTurn && battleProvider.isPlayerTurn &&
!battleProvider.player.isDead && !battleProvider.player.isDead &&
@ -501,7 +502,7 @@ class _BattleScreenState extends State<BattleScreen> {
context, context,
"DEF", "DEF",
Icons.shield, Icons.shield,
Colors.blueAccent, ThemeConfig.btnDefendActive,
ActionType.defend, ActionType.defend,
battleProvider.isPlayerTurn && battleProvider.isPlayerTurn &&
!battleProvider.player.isDead && !battleProvider.player.isDead &&
@ -527,7 +528,7 @@ class _BattleScreenState extends State<BattleScreen> {
}, },
child: Icon( child: Icon(
_showLogs ? Icons.visibility_off : Icons.visibility, _showLogs ? Icons.visibility_off : Icons.visibility,
color: Colors.white, color: ThemeConfig.textColorWhite,
), ),
), ),
), ),
@ -535,7 +536,7 @@ class _BattleScreenState extends State<BattleScreen> {
// Reward Popup // Reward Popup
if (battleProvider.showRewardPopup) if (battleProvider.showRewardPopup)
Container( Container(
color: Colors.black54, color: ThemeConfig.cardBgColor,
child: Center( child: Center(
child: SimpleDialog( child: SimpleDialog(
title: Row( title: Row(
@ -545,14 +546,14 @@ class _BattleScreenState extends State<BattleScreen> {
Row( Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Icon(Icons.monetization_on, color: Colors.amber, size: 18), Icon(Icons.monetization_on, color: ThemeConfig.statGoldColor, size: 18),
const SizedBox(width: 4), const SizedBox(width: 4),
Text( Text(
"${battleProvider.lastGoldReward} G", "${battleProvider.lastGoldReward} G",
style: TextStyle( style: TextStyle(
color: Colors.amber, color: ThemeConfig.statGoldColor,
fontSize: 14, fontSize: ThemeConfig.fontSizeBody,
fontWeight: FontWeight.bold, fontWeight: ThemeConfig.fontWeightBold,
), ),
), ),
], ],
@ -583,7 +584,7 @@ class _BattleScreenState extends State<BattleScreen> {
? ItemUtils.getRarityColor( ? ItemUtils.getRarityColor(
item.rarity, item.rarity,
) )
: Colors.grey, : ThemeConfig.rarityCommon,
), ),
), ),
child: Icon( child: Icon(
@ -596,10 +597,10 @@ class _BattleScreenState extends State<BattleScreen> {
Text( Text(
item.name, item.name,
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.bold, fontWeight: ThemeConfig.fontWeightBold,
fontSize: 16, fontSize: ThemeConfig.fontSizeLarge,
color: isSkip color: isSkip
? Colors.grey ? ThemeConfig.textColorGrey
: ItemUtils.getRarityColor( : ItemUtils.getRarityColor(
item.rarity), item.rarity),
), ),
@ -610,8 +611,8 @@ class _BattleScreenState extends State<BattleScreen> {
Text( Text(
item.description, item.description,
style: const TextStyle( style: const TextStyle(
fontSize: 12, fontSize: ThemeConfig.fontSizeMedium,
color: Colors.grey, color: ThemeConfig.textColorGrey,
), ),
), ),
], ],
@ -633,7 +634,7 @@ class _BattleScreenState extends State<BattleScreen> {
// Game Over Overlay // Game Over Overlay
if (battleProvider.player.isDead) if (battleProvider.player.isDead)
Container( Container(
color: Colors.black87, color: ThemeConfig.battleBg,
child: Center( child: Center(
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
@ -641,9 +642,9 @@ class _BattleScreenState extends State<BattleScreen> {
const Text( const Text(
"DEFEAT", "DEFEAT",
style: TextStyle( style: TextStyle(
color: Colors.red, color: ThemeConfig.statHpColor,
fontSize: 48, fontSize: ThemeConfig.fontSizeHuge,
fontWeight: FontWeight.bold, fontWeight: ThemeConfig.fontWeightBold,
letterSpacing: 4.0, letterSpacing: 4.0,
), ),
), ),
@ -667,8 +668,8 @@ class _BattleScreenState extends State<BattleScreen> {
child: const Text( child: const Text(
"Return to Main Menu", "Return to Main Menu",
style: TextStyle( style: TextStyle(
color: Colors.white, color: ThemeConfig.textColorWhite,
fontSize: 18, fontSize: ThemeConfig.fontSizeHeader,
), ),
), ),
), ),
@ -703,7 +704,7 @@ class _BattleScreenState extends State<BattleScreen> {
padding: const EdgeInsets.only(top: 4.0, bottom: 4.0), padding: const EdgeInsets.only(top: 4.0, bottom: 4.0),
child: Text( child: Text(
stats.join(", "), stats.join(", "),
style: const TextStyle(fontSize: 12, color: Colors.blueAccent), style: const TextStyle(fontSize: ThemeConfig.fontSizeMedium, color: ThemeConfig.statAtkColor),
), ),
), ),
if (effectTexts.isNotEmpty) if (effectTexts.isNotEmpty)
@ -711,7 +712,7 @@ class _BattleScreenState extends State<BattleScreen> {
padding: const EdgeInsets.only(bottom: 4.0), padding: const EdgeInsets.only(bottom: 4.0),
child: Text( child: Text(
effectTexts.join(", "), effectTexts.join(", "),
style: const TextStyle(fontSize: 11, color: Colors.orangeAccent), style: const TextStyle(fontSize: 11, color: ThemeConfig.rarityLegendary), // 11 is custom, keep or change? Let's use Small
), ),
), ),
], ],
@ -731,7 +732,7 @@ class _BattleScreenState extends State<BattleScreen> {
onPressed: isEnabled onPressed: isEnabled
? () => _showRiskLevelSelection(context, actionType) ? () => _showRiskLevelSelection(context, actionType)
: null, : null,
backgroundColor: isEnabled ? color : Colors.grey, backgroundColor: isEnabled ? color : ThemeConfig.btnDisabled,
child: Icon(icon), child: Icon(icon),
); );
} }

View File

@ -4,6 +4,7 @@ import '../providers/battle_provider.dart';
import '../game/data/player_table.dart'; import '../game/data/player_table.dart';
import 'main_wrapper.dart'; import 'main_wrapper.dart';
import '../widgets/responsive_container.dart'; import '../widgets/responsive_container.dart';
import '../game/config/theme_config.dart';
class CharacterSelectionScreen extends StatelessWidget { class CharacterSelectionScreen extends StatelessWidget {
const CharacterSelectionScreen({super.key}); const CharacterSelectionScreen({super.key});
@ -20,7 +21,7 @@ class CharacterSelectionScreen extends StatelessWidget {
} }
return Scaffold( return Scaffold(
backgroundColor: Colors.black, // Outer background backgroundColor: ThemeConfig.mainMenuBgTop, // Outer background
body: Center( body: Center(
child: ResponsiveContainer( child: ResponsiveContainer(
child: Scaffold( child: Scaffold(
@ -47,7 +48,7 @@ class CharacterSelectionScreen extends StatelessWidget {
); );
}, },
child: Card( child: Card(
color: Colors.blueGrey[800], color: ThemeConfig.selectionCardBg,
elevation: 8, elevation: 8,
child: Container( child: Container(
width: 300, width: 300,
@ -58,22 +59,22 @@ class CharacterSelectionScreen extends StatelessWidget {
const Icon( const Icon(
Icons.shield, Icons.shield,
size: 80, size: 80,
color: Colors.blue, color: ThemeConfig.selectionIconColor,
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
Text( Text(
warrior.name, warrior.name,
style: const TextStyle( style: const TextStyle(
fontSize: 24, fontSize: ThemeConfig.fontSizeTitle,
fontWeight: FontWeight.bold, fontWeight: ThemeConfig.fontWeightBold,
color: Colors.white, color: ThemeConfig.textColorWhite,
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
Text( Text(
warrior.description, warrior.description,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: const TextStyle(color: Colors.grey), style: const TextStyle(color: ThemeConfig.textColorGrey),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
const Divider(), const Divider(),
@ -84,19 +85,19 @@ class CharacterSelectionScreen extends StatelessWidget {
Text( Text(
"HP: ${warrior.baseHp}", "HP: ${warrior.baseHp}",
style: const TextStyle( style: const TextStyle(
fontWeight: FontWeight.bold, fontWeight: ThemeConfig.fontWeightBold,
), ),
), ),
Text( Text(
"ATK: ${warrior.baseAtk}", "ATK: ${warrior.baseAtk}",
style: const TextStyle( style: const TextStyle(
fontWeight: FontWeight.bold, fontWeight: ThemeConfig.fontWeightBold,
), ),
), ),
Text( Text(
"DEF: ${warrior.baseDefense}", "DEF: ${warrior.baseDefense}",
style: const TextStyle( style: const TextStyle(
fontWeight: FontWeight.bold, fontWeight: ThemeConfig.fontWeightBold,
), ),
), ),
], ],

View File

@ -4,6 +4,7 @@ import '../providers/battle_provider.dart';
import '../game/model/item.dart'; import '../game/model/item.dart';
import '../game/enums.dart'; import '../game/enums.dart';
import '../utils/item_utils.dart'; import '../utils/item_utils.dart';
import '../game/config/theme_config.dart';
class InventoryScreen extends StatelessWidget { class InventoryScreen extends StatelessWidget {
const InventoryScreen({super.key}); const InventoryScreen({super.key});
@ -45,12 +46,12 @@ class InventoryScreen extends StatelessWidget {
_buildStatItem( _buildStatItem(
"Gold", "Gold",
"${player.gold} G", "${player.gold} G",
color: Colors.amber, color: ThemeConfig.statGoldColor,
), ),
_buildStatItem( _buildStatItem(
"Luck", "Luck",
"${player.totalLuck}", "${player.totalLuck}",
color: Colors.green, color: ThemeConfig.statLuckColor,
), ),
], ],
), ),
@ -71,8 +72,8 @@ class InventoryScreen extends StatelessWidget {
const Text( const Text(
"Equipped Items", "Equipped Items",
style: TextStyle( style: TextStyle(
fontSize: 18, fontSize: ThemeConfig.fontSizeHeader,
fontWeight: FontWeight.bold, fontWeight: ThemeConfig.fontWeightBold,
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
@ -91,8 +92,8 @@ class InventoryScreen extends StatelessWidget {
: null, : null,
child: Card( child: Card(
color: item != null color: item != null
? Colors.blueGrey[600] ? ThemeConfig.equipmentCardBg
: Colors.grey[800], : ThemeConfig.emptySlotBg,
shape: item != null && shape: item != null &&
item.rarity != ItemRarity.magic item.rarity != ItemRarity.magic
? RoundedRectangleBorder( ? RoundedRectangleBorder(
@ -113,8 +114,8 @@ class InventoryScreen extends StatelessWidget {
child: Text( child: Text(
slot.name.toUpperCase(), slot.name.toUpperCase(),
style: const TextStyle( style: const TextStyle(
fontSize: 8, fontSize: ThemeConfig.fontSizeTiny,
fontWeight: FontWeight.bold, fontWeight: ThemeConfig.fontWeightBold,
color: Colors.white30, color: Colors.white30,
), ),
), ),
@ -130,7 +131,7 @@ class InventoryScreen extends StatelessWidget {
size: 40, size: 40,
color: item != null color: item != null
? ItemUtils.getColor(slot) ? ItemUtils.getColor(slot)
: Colors.grey, : ThemeConfig.textColorGrey,
), ),
), ),
), ),
@ -151,13 +152,13 @@ class InventoryScreen extends StatelessWidget {
item?.name ?? "Empty", item?.name ?? "Empty",
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle( style: TextStyle(
fontSize: 11, fontSize: ThemeConfig.fontSizeSmall,
fontWeight: FontWeight.bold, fontWeight: ThemeConfig.fontWeightBold,
color: item != null color: item != null
? ItemUtils.getRarityColor( ? ItemUtils.getRarityColor(
item.rarity, item.rarity,
) )
: Colors.grey, : ThemeConfig.textColorGrey,
), ),
maxLines: 2, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
@ -194,8 +195,8 @@ class InventoryScreen extends StatelessWidget {
child: Text( child: Text(
"Bag (${player.inventory.length}/${player.maxInventorySize})", "Bag (${player.inventory.length}/${player.maxInventorySize})",
style: const TextStyle( style: const TextStyle(
fontSize: 18, fontSize: ThemeConfig.fontSizeHeader,
fontWeight: FontWeight.bold, fontWeight: ThemeConfig.fontWeightBold,
), ),
), ),
), ),
@ -218,7 +219,7 @@ class InventoryScreen extends StatelessWidget {
_showItemActionDialog(context, battleProvider, item); _showItemActionDialog(context, battleProvider, item);
}, },
child: Card( child: Card(
color: Colors.blueGrey[700], color: ThemeConfig.inventoryCardBg,
shape: item.rarity != ItemRarity.magic shape: item.rarity != ItemRarity.magic
? RoundedRectangleBorder( ? RoundedRectangleBorder(
side: BorderSide( side: BorderSide(
@ -258,8 +259,8 @@ class InventoryScreen extends StatelessWidget {
item.name, item.name,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle( style: TextStyle(
fontSize: 11, fontSize: ThemeConfig.fontSizeSmall,
fontWeight: FontWeight.bold, fontWeight: ThemeConfig.fontWeightBold,
color: ItemUtils.getRarityColor( color: ItemUtils.getRarityColor(
item.rarity, item.rarity,
), ),
@ -284,11 +285,11 @@ class InventoryScreen extends StatelessWidget {
// Empty slot // Empty slot
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all(color: Colors.grey), border: Border.all(color: ThemeConfig.textColorGrey),
color: Colors.grey[800], color: ThemeConfig.emptySlotBg,
), ),
child: const Center( child: const Center(
child: Icon(Icons.add_box, color: Colors.grey), child: Icon(Icons.add_box, color: ThemeConfig.textColorGrey),
), ),
); );
} }
@ -305,11 +306,11 @@ class InventoryScreen extends StatelessWidget {
Widget _buildStatItem(String label, String value, {Color? color}) { Widget _buildStatItem(String label, String value, {Color? color}) {
return Column( return Column(
children: [ children: [
Text(label, style: const TextStyle(color: Colors.grey, fontSize: 12)), Text(label, style: const TextStyle(color: ThemeConfig.textColorGrey, fontSize: 12)),
Text( Text(
value, value,
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.bold, fontWeight: ThemeConfig.fontWeightBold,
fontSize: 16, fontSize: 16,
color: color, color: color,
), ),
@ -340,7 +341,7 @@ class InventoryScreen extends StatelessWidget {
padding: EdgeInsets.symmetric(vertical: 8.0), padding: EdgeInsets.symmetric(vertical: 8.0),
child: Row( child: Row(
children: [ children: [
Icon(Icons.shield, color: Colors.blue), Icon(Icons.shield, color: ThemeConfig.btnDefendActive),
SizedBox(width: 10), SizedBox(width: 10),
Text("Equip"), Text("Equip"),
], ],
@ -357,7 +358,7 @@ class InventoryScreen extends StatelessWidget {
padding: const EdgeInsets.symmetric(vertical: 8.0), padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Row( child: Row(
children: [ children: [
const Icon(Icons.attach_money, color: Colors.amber), const Icon(Icons.attach_money, color: ThemeConfig.statGoldColor),
const SizedBox(width: 10), const SizedBox(width: 10),
Text("Sell (${item.price} G)"), Text("Sell (${item.price} G)"),
], ],
@ -373,7 +374,7 @@ class InventoryScreen extends StatelessWidget {
padding: EdgeInsets.symmetric(vertical: 8.0), padding: EdgeInsets.symmetric(vertical: 8.0),
child: Row( child: Row(
children: [ children: [
Icon(Icons.delete, color: Colors.red), Icon(Icons.delete, color: ThemeConfig.btnActionActive),
SizedBox(width: 10), SizedBox(width: 10),
Text("Discard"), Text("Discard"),
], ],
@ -401,7 +402,7 @@ class InventoryScreen extends StatelessWidget {
child: const Text("Cancel"), child: const Text("Cancel"),
), ),
ElevatedButton( ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: Colors.amber), style: ElevatedButton.styleFrom(backgroundColor: ThemeConfig.statGoldColor),
onPressed: () { onPressed: () {
provider.sellItem(item); provider.sellItem(item);
Navigator.pop(ctx); Navigator.pop(ctx);
@ -429,7 +430,7 @@ class InventoryScreen extends StatelessWidget {
child: const Text("Cancel"), child: const Text("Cancel"),
), ),
ElevatedButton( ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: Colors.red), style: ElevatedButton.styleFrom(backgroundColor: ThemeConfig.btnActionActive),
onPressed: () { onPressed: () {
provider.discardItem(item); provider.discardItem(item);
Navigator.pop(ctx); Navigator.pop(ctx);
@ -475,12 +476,12 @@ class InventoryScreen extends StatelessWidget {
children: [ children: [
Text( Text(
"Equip ${newItem.name}?", "Equip ${newItem.name}?",
style: const TextStyle(fontWeight: FontWeight.bold), style: const TextStyle(fontWeight: ThemeConfig.fontWeightBold),
), ),
if (oldItem != null) if (oldItem != null)
Text( Text(
"Replaces ${oldItem.name}", "Replaces ${oldItem.name}",
style: const TextStyle(fontSize: 12, color: Colors.grey), style: const TextStyle(fontSize: 12, color: ThemeConfig.textColorGrey),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
_buildStatChangeRow("Max HP", currentMaxHp, newMaxHp), _buildStatChangeRow("Max HP", currentMaxHp, newMaxHp),
@ -544,7 +545,7 @@ class InventoryScreen extends StatelessWidget {
children: [ children: [
Text( Text(
"Unequip ${itemToUnequip.name}?", "Unequip ${itemToUnequip.name}?",
style: const TextStyle(fontWeight: FontWeight.bold), style: const TextStyle(fontWeight: ThemeConfig.fontWeightBold),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
_buildStatChangeRow("Max HP", currentMaxHp, newMaxHp), _buildStatChangeRow("Max HP", currentMaxHp, newMaxHp),
@ -573,8 +574,8 @@ class InventoryScreen extends StatelessWidget {
Widget _buildStatChangeRow(String label, int oldVal, int newVal) { Widget _buildStatChangeRow(String label, int oldVal, int newVal) {
int diff = newVal - oldVal; int diff = newVal - oldVal;
Color color = diff > 0 Color color = diff > 0
? Colors.green ? ThemeConfig.statDiffPositive
: (diff < 0 ? Colors.red : Colors.grey); : (diff < 0 ? ThemeConfig.statDiffNegative : ThemeConfig.statDiffNeutral);
String diffText = diff > 0 ? "(+$diff)" : (diff < 0 ? "($diff)" : ""); String diffText = diff > 0 ? "(+$diff)" : (diff < 0 ? "($diff)" : "");
return Padding( return Padding(
@ -585,11 +586,11 @@ class InventoryScreen extends StatelessWidget {
Text(label), Text(label),
Row( Row(
children: [ children: [
Text("$oldVal", style: const TextStyle(color: Colors.grey)), Text("$oldVal", style: const TextStyle(color: ThemeConfig.textColorGrey)),
const Icon(Icons.arrow_right, size: 16, color: Colors.grey), const Icon(Icons.arrow_right, size: 16, color: ThemeConfig.textColorGrey),
Text( Text(
"$newVal", "$newVal",
style: const TextStyle(fontWeight: FontWeight.bold), style: const TextStyle(fontWeight: ThemeConfig.fontWeightBold),
), ),
const SizedBox(width: 4), const SizedBox(width: 4),
Text( Text(
@ -597,7 +598,7 @@ class InventoryScreen extends StatelessWidget {
style: TextStyle( style: TextStyle(
color: color, color: color,
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.bold, fontWeight: ThemeConfig.fontWeightBold,
), ),
), ),
], ],
@ -626,7 +627,7 @@ class InventoryScreen extends StatelessWidget {
padding: const EdgeInsets.only(top: 2.0, bottom: 2.0), padding: const EdgeInsets.only(top: 2.0, bottom: 2.0),
child: Text( child: Text(
stats.join(", "), stats.join(", "),
style: const TextStyle(fontSize: 10, color: Colors.blueAccent), style: const TextStyle(fontSize: ThemeConfig.fontSizeSmall, color: ThemeConfig.statAtkColor),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
), ),
@ -635,8 +636,7 @@ class InventoryScreen extends StatelessWidget {
padding: const EdgeInsets.only(bottom: 2.0), padding: const EdgeInsets.only(bottom: 2.0),
child: Text( child: Text(
effectTexts.join("\n"), effectTexts.join("\n"),
style: const TextStyle(fontSize: 9, color: Colors.orangeAccent), style: const TextStyle(fontSize: ThemeConfig.fontSizeTiny, color: ThemeConfig.rarityLegendary),
textAlign: TextAlign.center,
), ),
), ),
], ],

View File

@ -5,6 +5,7 @@ import 'main_wrapper.dart';
import '../widgets/responsive_container.dart'; import '../widgets/responsive_container.dart';
import '../game/save_manager.dart'; import '../game/save_manager.dart';
import '../providers/battle_provider.dart'; import '../providers/battle_provider.dart';
import '../game/config/theme_config.dart';
class MainMenuScreen extends StatefulWidget { class MainMenuScreen extends StatefulWidget {
const MainMenuScreen({super.key}); const MainMenuScreen({super.key});
@ -58,7 +59,10 @@ class _MainMenuScreenState extends State<MainMenuScreen> {
gradient: LinearGradient( gradient: LinearGradient(
begin: Alignment.topCenter, begin: Alignment.topCenter,
end: Alignment.bottomCenter, end: Alignment.bottomCenter,
colors: [Colors.black, Colors.blueGrey[900]!], colors: [
ThemeConfig.mainMenuBgTop,
ThemeConfig.mainMenuBgBottom,
],
), ),
), ),
child: ResponsiveContainer( child: ResponsiveContainer(
@ -67,23 +71,27 @@ class _MainMenuScreenState extends State<MainMenuScreen> {
: Column( : Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
const Icon(Icons.gavel, size: 100, color: Colors.amber), const Icon(
Icons.gavel,
size: 100,
color: ThemeConfig.mainIconColor,
),
const SizedBox(height: 20), const SizedBox(height: 20),
const Text( const Text(
"COLOSSEUM'S CHOICE", "COLOSSEUM'S CHOICE",
style: TextStyle( style: TextStyle(
fontSize: 32, fontSize: ThemeConfig.fontSizeHero,
fontWeight: FontWeight.bold, fontWeight: ThemeConfig.fontWeightBold,
letterSpacing: 2.0, letterSpacing: 2.0,
color: Colors.white, color: ThemeConfig.mainTitleColor,
), ),
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
const Text( const Text(
"Rise as a Legend", "Rise as a Legend",
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: ThemeConfig.fontSizeLarge,
color: Colors.grey, color: ThemeConfig.subTitleColor,
fontStyle: FontStyle.italic, fontStyle: FontStyle.italic,
), ),
), ),
@ -96,11 +104,11 @@ class _MainMenuScreenState extends State<MainMenuScreen> {
horizontal: 50, horizontal: 50,
vertical: 15, vertical: 15,
), ),
backgroundColor: Colors.blueAccent, backgroundColor: ThemeConfig.btnContinueBg,
foregroundColor: Colors.white, foregroundColor: ThemeConfig.btnContinueText,
textStyle: const TextStyle( textStyle: const TextStyle(
fontSize: 20, fontSize: ThemeConfig.fontSizeXLarge,
fontWeight: FontWeight.bold, fontWeight: ThemeConfig.fontWeightBold,
), ),
), ),
child: const Text("CONTINUE"), child: const Text("CONTINUE"),
@ -123,11 +131,11 @@ class _MainMenuScreenState extends State<MainMenuScreen> {
horizontal: 50, horizontal: 50,
vertical: 15, vertical: 15,
), ),
backgroundColor: Colors.amber[700], backgroundColor: ThemeConfig.btnNewGameBg,
foregroundColor: Colors.black, foregroundColor: ThemeConfig.btnNewGameText,
textStyle: const TextStyle( textStyle: const TextStyle(
fontSize: 20, fontSize: ThemeConfig.fontSizeXLarge,
fontWeight: FontWeight.bold, fontWeight: ThemeConfig.fontWeightBold,
), ),
), ),
child: const Text("NEW GAME"), child: const Text("NEW GAME"),

View File

@ -3,6 +3,7 @@ import 'battle_screen.dart';
import 'inventory_screen.dart'; import 'inventory_screen.dart';
import 'settings_screen.dart'; import 'settings_screen.dart';
import '../widgets/responsive_container.dart'; import '../widgets/responsive_container.dart';
import '../game/config/theme_config.dart';
class MainWrapper extends StatefulWidget { class MainWrapper extends StatefulWidget {
const MainWrapper({super.key}); const MainWrapper({super.key});
@ -23,7 +24,7 @@ class _MainWrapperState extends State<MainWrapper> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
backgroundColor: Colors.black, // Outer background for web backgroundColor: ThemeConfig.mainMenuBgTop, // Outer background for web
body: Center( body: Center(
child: ResponsiveContainer( child: ResponsiveContainer(
child: Scaffold( child: Scaffold(

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../providers/battle_provider.dart'; import '../providers/battle_provider.dart';
import 'main_menu_screen.dart'; import 'main_menu_screen.dart';
import '../game/config/theme_config.dart';
class SettingsScreen extends StatelessWidget { class SettingsScreen extends StatelessWidget {
const SettingsScreen({super.key}); const SettingsScreen({super.key});
@ -15,28 +16,28 @@ class SettingsScreen extends StatelessWidget {
const Text( const Text(
'Settings', 'Settings',
style: TextStyle( style: TextStyle(
fontSize: 24, fontSize: ThemeConfig.fontSizeTitle,
fontWeight: FontWeight.bold, fontWeight: ThemeConfig.fontWeightBold,
color: Colors.white, color: ThemeConfig.textColorWhite,
), ),
), ),
const SizedBox(height: 40), const SizedBox(height: 40),
// Placeholder for future settings // Placeholder for future settings
const Text( const Text(
'Effect Intensity: Normal', 'Effect Intensity: Normal',
style: TextStyle(color: Colors.white70), style: TextStyle(color: ThemeConfig.textColorWhite),
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
const Text( const Text(
'Volume: 100%', 'Volume: 100%',
style: TextStyle(color: Colors.white70), style: TextStyle(color: ThemeConfig.textColorWhite),
), ),
const SizedBox(height: 40), const SizedBox(height: 40),
// Restart Button // Restart Button
ElevatedButton( ElevatedButton(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Colors.orange, backgroundColor: ThemeConfig.btnRestartBg,
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 12), padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 12),
), ),
onPressed: () { onPressed: () {
@ -63,7 +64,7 @@ class SettingsScreen extends StatelessWidget {
// Return to Main Menu Button // Return to Main Menu Button
ElevatedButton( ElevatedButton(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Colors.red, backgroundColor: ThemeConfig.btnReturnMenuBg,
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 12), padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 12),
), ),
onPressed: () { onPressed: () {

View File

@ -90,9 +90,9 @@ class CharacterStatusCard extends StatelessWidget {
}).toList(), }).toList(),
), ),
), ),
Text("ATK: ${character.totalAtk}"), Text("ATK: ${character.totalAtk}", style: const TextStyle(color: ThemeConfig.textColorWhite)),
Text("DEF: ${character.totalDefense}"), Text("DEF: ${character.totalDefense}", style: const TextStyle(color: ThemeConfig.textColorWhite)),
Text("LUCK: ${character.totalLuck}"), Text("LUCK: ${character.totalLuck}", style: const TextStyle(color: ThemeConfig.textColorWhite)),
], ],
), ),
), ),
@ -135,16 +135,16 @@ class CharacterStatusCard extends StatelessWidget {
child: Container( child: Container(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.black54, color: ThemeConfig.enemyIntentBg,
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.redAccent), border: Border.all(color: ThemeConfig.enemyIntentBorder),
), ),
child: Column( child: Column(
children: [ children: [
Text( Text(
"INTENT", "INTENT",
style: TextStyle( style: TextStyle(
color: Colors.redAccent, color: ThemeConfig.enemyIntentBorder,
fontSize: 10, fontSize: 10,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@ -156,14 +156,14 @@ class CharacterStatusCard extends StatelessWidget {
intent.type == EnemyActionType.attack intent.type == EnemyActionType.attack
? Icons.flash_on ? Icons.flash_on
: Icons.shield, : Icons.shield,
color: Colors.yellow, color: ThemeConfig.rarityRare, // Yellow
size: 16, size: 16,
), ),
const SizedBox(width: 4), const SizedBox(width: 4),
Text( Text(
intent.description, intent.description,
style: const TextStyle( style: const TextStyle(
color: Colors.white, color: ThemeConfig.textColorWhite,
fontSize: 12, fontSize: 12,
), ),
), ),

View File

@ -3,6 +3,7 @@ import '../../providers/battle_provider.dart';
import '../../game/model/item.dart'; import '../../game/model/item.dart';
import '../../utils/item_utils.dart'; import '../../utils/item_utils.dart';
import '../../game/enums.dart'; import '../../game/enums.dart';
import '../../game/config/theme_config.dart';
class ShopUI extends StatelessWidget { class ShopUI extends StatelessWidget {
final BattleProvider battleProvider; final BattleProvider battleProvider;
@ -15,7 +16,7 @@ class ShopUI extends StatelessWidget {
final shopItems = battleProvider.currentStage.shopItems; final shopItems = battleProvider.currentStage.shopItems;
return Container( return Container(
color: Colors.black87, color: ThemeConfig.shopBg,
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Column( child: Column(
children: [ children: [
@ -25,22 +26,22 @@ class ShopUI extends StatelessWidget {
children: [ children: [
const Row( const Row(
children: [ children: [
Icon(Icons.store, size: 32, color: Colors.amber), Icon(Icons.store, size: 32, color: ThemeConfig.mainIconColor),
SizedBox(width: 8), SizedBox(width: 8),
Text( Text(
"Merchant", "Merchant",
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: Colors.white), style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: ThemeConfig.textColorWhite),
), ),
], ],
), ),
Row( Row(
children: [ children: [
const Icon(Icons.monetization_on, color: Colors.amber), const Icon(Icons.monetization_on, color: ThemeConfig.statGoldColor),
const SizedBox(width: 4), const SizedBox(width: 4),
Text( Text(
"${player.gold} G", "${player.gold} G",
style: const TextStyle( style: const TextStyle(
color: Colors.amber, color: ThemeConfig.statGoldColor,
fontSize: 20, fontSize: 20,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
@ -49,7 +50,7 @@ class ShopUI extends StatelessWidget {
), ),
], ],
), ),
const Divider(color: Colors.grey), const Divider(color: ThemeConfig.textColorGrey),
const SizedBox(height: 16), const SizedBox(height: 16),
// Shop Items Grid // Shop Items Grid
@ -58,7 +59,7 @@ class ShopUI extends StatelessWidget {
? const Center( ? const Center(
child: Text( child: Text(
"Sold Out", "Sold Out",
style: TextStyle(color: Colors.grey, fontSize: 24), style: TextStyle(color: ThemeConfig.textColorGrey, fontSize: 24),
), ),
) )
: GridView.builder( : GridView.builder(
@ -76,7 +77,7 @@ class ShopUI extends StatelessWidget {
return InkWell( return InkWell(
onTap: () => _showBuyConfirmation(context, item), onTap: () => _showBuyConfirmation(context, item),
child: Card( child: Card(
color: Colors.blueGrey[800], color: ThemeConfig.shopItemCardBg,
shape: item.rarity != ItemRarity.magic shape: item.rarity != ItemRarity.magic
? RoundedRectangleBorder( ? RoundedRectangleBorder(
side: BorderSide( side: BorderSide(
@ -129,7 +130,7 @@ class ShopUI extends StatelessWidget {
height: 32, height: 32,
child: ElevatedButton( child: ElevatedButton(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: canBuy ? Colors.amber : Colors.grey, backgroundColor: canBuy ? ThemeConfig.statGoldColor : ThemeConfig.btnDisabled,
foregroundColor: Colors.black, foregroundColor: Colors.black,
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
), ),
@ -159,28 +160,28 @@ class ShopUI extends StatelessWidget {
children: [ children: [
ElevatedButton.icon( ElevatedButton.icon(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Colors.blueGrey, backgroundColor: ThemeConfig.btnRerollBg,
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
), ),
onPressed: player.gold >= 50 onPressed: player.gold >= 50
? () => battleProvider.rerollShopItems() ? () => battleProvider.rerollShopItems()
: null, : null,
icon: const Icon(Icons.refresh, color: Colors.white), icon: const Icon(Icons.refresh, color: ThemeConfig.textColorWhite),
label: const Text( label: const Text(
"Reroll (50 G)", "Reroll (50 G)",
style: TextStyle(color: Colors.white), style: TextStyle(color: ThemeConfig.textColorWhite),
), ),
), ),
ElevatedButton.icon( ElevatedButton.icon(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Colors.redAccent, backgroundColor: ThemeConfig.btnLeaveBg,
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
), ),
onPressed: () => battleProvider.proceedToNextStage(), onPressed: () => battleProvider.proceedToNextStage(),
icon: const Icon(Icons.exit_to_app, color: Colors.white), icon: const Icon(Icons.exit_to_app, color: ThemeConfig.textColorWhite),
label: const Text( label: const Text(
"Leave Shop", "Leave Shop",
style: TextStyle(color: Colors.white), style: TextStyle(color: ThemeConfig.textColorWhite),
), ),
), ),
], ],
@ -204,7 +205,7 @@ class ShopUI extends StatelessWidget {
child: const Text("Cancel"), child: const Text("Cancel"),
), ),
ElevatedButton( ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: Colors.amber), style: ElevatedButton.styleFrom(backgroundColor: ThemeConfig.statGoldColor),
onPressed: () { onPressed: () {
battleProvider.buyItem(item); battleProvider.buyItem(item);
Navigator.pop(ctx); Navigator.pop(ctx);
@ -237,7 +238,7 @@ class ShopUI extends StatelessWidget {
if (item.effects.isNotEmpty) if (item.effects.isNotEmpty)
Text( Text(
item.effects.first.type.name.toUpperCase(), item.effects.first.type.name.toUpperCase(),
style: const TextStyle(fontSize: 9, color: Colors.orangeAccent), style: const TextStyle(fontSize: 9, color: ThemeConfig.rarityLegendary),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
], ],
@ -256,11 +257,11 @@ class RestUI extends StatelessWidget {
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
const Icon(Icons.local_hotel, size: 64, color: Colors.blue), const Icon(Icons.local_hotel, size: 64, color: ThemeConfig.btnRestBg),
const SizedBox(height: 16), const SizedBox(height: 16),
const Text("Rest Area", style: TextStyle(fontSize: 24)), const Text("Rest Area", style: TextStyle(fontSize: 24, color: ThemeConfig.textColorWhite)),
const SizedBox(height: 8), const SizedBox(height: 8),
const Text("Take a breath and heal."), const Text("Take a breath and heal.", style: TextStyle(color: ThemeConfig.textColorWhite)),
const SizedBox(height: 32), const SizedBox(height: 32),
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {

View File

@ -115,6 +115,7 @@
- **Prompt Driven Development:** `prompt/XX_description.md` 유지. - **Prompt Driven Development:** `prompt/XX_description.md` 유지.
- **Language:** **모든 프롬프트 파일(prompt/XX_...)은 반드시 한국어(Korean)로 작성해야 합니다.** - **Language:** **모든 프롬프트 파일(prompt/XX_...)은 반드시 한국어(Korean)로 작성해야 합니다.**
- **Config Management:** 하드코딩되는 값들은 `config` 폴더 내 파일들(`lib/game/config/` 등)에서 통합 관리할 수 있도록 작성해야 합니다.
- **State Management:** `Provider` + `Stream` (이벤트성 데이터). - **State Management:** `Provider` + `Stream` (이벤트성 데이터).
- **Data:** JSON 기반. - **Data:** JSON 기반.
@ -158,3 +159,4 @@
- [x] 42_item_rarity_and_tier.md - [x] 42_item_rarity_and_tier.md
- [x] 43_shop_system.md - [x] 43_shop_system.md
- [x] 44_settings_and_local_storage.md - [x] 44_settings_and_local_storage.md
- [x] 45_config_refactoring.md

View File

@ -0,0 +1,38 @@
# 45. Config & UI 전면 리팩토링 (Config & UI Refactoring)
## 1. 목표 (Goal)
- 프로젝트 전반에 산재된 하드코딩된 값(정적 상수, 색상, 폰트)을 설정 파일(`GameConfig`, `ThemeConfig`)로 통합 관리합니다.
- 모든 화면(`MainMenu`, `Battle`, `Inventory`, `Settings`, `CharacterSelection`)의 UI 스타일을 통일합니다.
## 2. 구현 상세 (Implementation Details)
### A. 게임 밸런스 설정 (`GameConfig`)
- **파일:** `lib/game/config/game_config.dart`
- **내용:**
- **인벤토리/경제:** 최대 크기, 시작 골드, 상점 비용 및 리롤 등.
- **스테이지:** 구간별 설정, 티어 분포.
- **전투:** 회복율, 데미지 배율, 방어도 감소, 애니메이션 딜레이 등.
- **시스템:** 저장 키 (`saveKey`).
- **적용:** `BattleProvider`, `Entity`, `SaveManager`의 매직 넘버 제거.
### B. 테마 및 스타일 설정 (`ThemeConfig`)
- **파일:** `lib/game/config/theme_config.dart`
- **색상 (Colors):**
- 메인 메뉴(배경, 버튼), 전투 화면(배경, UI, 리스크), 상점/휴식 화면, 아이템 등급 등 모든 색상 정의.
- **폰트 (Fonts):**
- **Size:** `Tiny`(8.0) ~ `Huge`(48.0) 등 10단계 정의.
- **Weight:** `Normal`, `Bold` 정의.
### C. 화면별 리팩토링 (Screen Refactoring)
- **`lib/screens/``lib/widgets/` 전체:**
- **`BattleScreen` & Widgets:** 로그, 리스크 다이얼로그, 캐릭터 카드, 게임 오버, 보상 팝업 등.
- **`MainMenuScreen`:** 타이틀, 버튼, 배경.
- **`InventoryScreen`:** 슬롯, 스탯 텍스트, 팝업.
- **`CharacterSelectionScreen`:** 카드, 텍스트 스타일.
- **`SettingsScreen`:** 텍스트, 버튼.
- **`MainWrapper`:** 공통 배경색 적용.
## 3. 결과 (Result)
- **중앙 관리:** 게임의 수치 밸런스와 디자인 테마를 각각 `GameConfig``ThemeConfig`에서 손쉽게 수정 가능해졌습니다.
- **일관성:** 모든 화면에서 통일된 색상 팔레트와 폰트 스케일을 사용하여 UI 완성도가 향상되었습니다.
- **유지보수:** 하드코딩된 값들이 제거되어 코드 가독성 및 유지보수성이 대폭 개선되었습니다.