From 3b9fa47f0ab40b7a4ec87efb1b53465d05d89d56 Mon Sep 17 00:00:00 2001 From: horoli Date: Wed, 4 Feb 2026 01:44:38 +0900 Subject: [PATCH] update --- assets/images/icons/accessories/ring_01.png | Bin 0 -> 465 bytes assets/images/icons/accessories/ring_02.png | Bin 0 -> 556 bytes lib/providers/battle_provider.dart | 59 ++++++++---- lib/utils/item_utils.dart | 10 +- lib/widgets/battle/battle_controls.dart | 55 +++-------- lib/widgets/common/item_card_widget.dart | 96 ++++++++++++-------- pubspec.yaml | 6 ++ 7 files changed, 126 insertions(+), 100 deletions(-) create mode 100644 assets/images/icons/accessories/ring_01.png create mode 100644 assets/images/icons/accessories/ring_02.png diff --git a/assets/images/icons/accessories/ring_01.png b/assets/images/icons/accessories/ring_01.png new file mode 100644 index 0000000000000000000000000000000000000000..c0d5dba3713eef76d474810187a4eb7fe2ccef54 GIT binary patch literal 465 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vO2U$sR$z3=CDO3=9p;3=BX21L>Cx z45bDP46hOx7_4S6Fo@?*ia+WGR4WnS6XN>+|9^2oshjNq7gBWoU-0e=3;Ms`3@FGW zp*|m|hOs2bFPOpM*^M+1C&}C0g`tC0)&t1lEbxddW?nC}Q!>*kaci)8Ufv7TpaHj`Br`X)xFj*R0E-?Ah$Z=7nN|Sx6nMHghG?8W z+jo(-L4n65k=dZFKC;oGfwPcNUBPhse~z#{LIq6s_;*fUyw))IVXLD1!T&|+IVycg zohyaRbyea$*Q^n@|GoE$xr~)gz@1wRte?!8MZ``V;%um9_PBUBWQSR;j>pnjm$;Jd dsm9K0VBe}D&5_>u-vwwlgQu&X%Q~loCIG&;ikbib literal 0 HcmV?d00001 diff --git a/assets/images/icons/accessories/ring_02.png b/assets/images/icons/accessories/ring_02.png new file mode 100644 index 0000000000000000000000000000000000000000..88810399c8d8de93443247e1a6133952a58afa48 GIT binary patch literal 556 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7e6l0AZa85pWm85kOx85n;42huMY z7)lKo7+xhXFj&oCU=Yur6o1qWsMajNC&cyt|Nr8GQWi7*8#i9hd-vaQ)`R4`|1A<0 zx-@S8|EUxx8-MElnLO{lu%OrT?IP!G|G(et|I_tmE&nutCNP!+`2{mLJiCzw;v{*y zyD)UH%6b4foCO|{#S9GG!XV7ZFl&wkP>{XE)7O>#6%#Wb6Nf7A$^xK}YKdz^NlIc# zs#S7PDv)9@GB7gJH89dOFb**^u`)2SGBDOQFt9Q(@ZNRa5Jf|7eoAIqC2kE?&&zv( z8Z_WGlw{_n7MCRE7GTk10kI_iE7J;~p0%DXjv*GO_f9@7bVz~6wVGRG>Xc*u|3@;H z9h;`IE6{jDL$$@>4>Oki7yDN9xsq@1!L!L-hPrZ%HJye_RG+@lO!@hBn%76OMww=d zF6{-T<_hamgjP&#$Yov;7GY_8E6^kT8p}J|`|LFm5<6Wv3T7Q(;9*)T!Rqx<@`1j@ zx#QeHyTcgV9#0Hta=Rj~c>U-vBdLq&iO>9-Zp~J+e&3=XWL;TYtp7jR=l%b)>t8Vz XRIx9xd9!m9(BTZ8u6{1-oD!M literal 0 HcmV?d00001 diff --git a/lib/providers/battle_provider.dart b/lib/providers/battle_provider.dart index c848411..114cdc9 100644 --- a/lib/providers/battle_provider.dart +++ b/lib/providers/battle_provider.dart @@ -15,6 +15,13 @@ import 'shop_provider.dart'; import '../game/logic.dart'; +class TurnEffectResult { + final bool canAct; + final bool effectTriggered; + + TurnEffectResult({required this.canAct, required this.effectTriggered}); +} + class EnemyIntent { final EnemyActionType type; final int value; @@ -225,8 +232,9 @@ class BattleProvider with ChangeNotifier { /// Handle player's action choice Future playerAction(ActionType type, RiskLevel risk) async { - if (!isPlayerTurn || player.isDead || enemy.isDead || showRewardPopup) + if (!isPlayerTurn || player.isDead || enemy.isDead || showRewardPopup) { return; + } // 0. Ensure Pre-emptive Enemy Defense is applied (if not already via animation) applyPendingEnemyDefense(); @@ -245,14 +253,19 @@ class BattleProvider with ChangeNotifier { notifyListeners(); // 2. Process Start-of-Turn Effects (Stun, Bleed) - bool canAct = _processStartTurnEffects(player); + TurnEffectResult turnEffect = _processStartTurnEffects(player); if (player.isDead) { await _onDefeat(); return; } - if (!canAct) { + // If a visual effect occurred (bleed, stun), wait a bit before action + if (turnEffect.effectTriggered) { + await Future.delayed(const Duration(milliseconds: 800)); + } + + if (!turnEffect.canAct) { _endPlayerTurn(); // Skip turn if stunned return; } @@ -474,11 +487,12 @@ class BattleProvider with ChangeNotifier { } /// Check Status Effects at Start of Turn - bool _processStartTurnEffects(Character character) { + TurnEffectResult _processStartTurnEffects(Character character) { final result = CombatCalculator.processStartTurnEffects(character); int totalBleed = result['bleedDamage']; bool isStunned = result['isStunned']; + bool effectTriggered = false; // 1. Bleed Damage if (totalBleed > 0) { @@ -496,14 +510,19 @@ class BattleProvider with ChangeNotifier { type: DamageType.bleed, ), ); + effectTriggered = true; } // 2. Stun Check if (isStunned) { _addLog("${character.name} is stunned!"); + effectTriggered = true; } - return !isStunned; + return TurnEffectResult( + canAct: !isStunned, + effectTriggered: effectTriggered, + ); } // --- Turn Management Phases --- @@ -522,14 +541,19 @@ class BattleProvider with ChangeNotifier { } // Process Start-of-Turn Effects - bool canAct = _processStartTurnEffects(enemy); + TurnEffectResult turnEffect = _processStartTurnEffects(enemy); if (enemy.isDead) { _onVictory(); return; } - if (canAct && currentEnemyIntent != null) { + // If a visual effect occurred (bleed, stun), wait a bit before action + if (turnEffect.effectTriggered) { + await Future.delayed(const Duration(milliseconds: 800)); + } + + if (turnEffect.canAct && currentEnemyIntent != null) { final intent = currentEnemyIntent!; if (intent.type == EnemyActionType.defend) { @@ -617,7 +641,7 @@ class BattleProvider with ChangeNotifier { return; } } - } else if (!canAct) { + } else if (!turnEffect.canAct) { // If cannot act (stunned) _addLog("Enemy is stunned and cannot act!"); int tid = _turnTransactionId; @@ -674,10 +698,11 @@ class BattleProvider with ChangeNotifier { _addLog("Choose a reward."); ItemTier currentTier = ItemTier.tier1; - if (stage > GameConfig.tier2StageMax) + if (stage > GameConfig.tier2StageMax) { currentTier = ItemTier.tier3; - else if (stage > GameConfig.tier1StageMax) + } else if (stage > GameConfig.tier1StageMax) { currentTier = ItemTier.tier2; + } rewardOptions = []; @@ -910,16 +935,12 @@ class BattleProvider with ChangeNotifier { if (canAttack && canDefend) { // Both options available: Use configured probability isAttack = _random.nextDouble() < BattleConfig.enemyAttackChance; - } else if (canAttack) { - // Must attack - isAttack = true; } else if (canDefend) { // Must defend isAttack = false; } else { - // Both forbidden (Rare case, effectively stunned but not via Stun status) - // Default to Defend as a fallback, outcomes will be handled by stats/luck - isAttack = false; + // Must attack (default) or both forbidden (defaults to attack currently) + isAttack = true; } // Decide Risk Level @@ -1113,7 +1134,11 @@ class BattleProvider with ChangeNotifier { } /// Tries to applyStatus effects from attacker's equipment to the target. - void _tryApplyStatusEffects(Character attacker, Character target, int damageToHp) { + void _tryApplyStatusEffects( + Character attacker, + Character target, + int damageToHp, + ) { List effectsToApply = CombatCalculator.getAppliedEffects( attacker, random: _random, // Pass injected random diff --git a/lib/utils/item_utils.dart b/lib/utils/item_utils.dart index e7aba5a..c01dd4b 100644 --- a/lib/utils/item_utils.dart +++ b/lib/utils/item_utils.dart @@ -21,15 +21,15 @@ class ItemUtils { static String getIconPath(EquipmentSlot slot) { switch (slot) { case EquipmentSlot.weapon: - return 'assets/data/icon/icon_weapon.png'; + return 'assets/images/icons/weapons/sword_02.png'; case EquipmentSlot.shield: - return 'assets/data/icon/icon_shield.png'; + return 'assets/images/icons/subweapons/shield_01.png'; case EquipmentSlot.armor: - return 'assets/data/icon/icon_armor.png'; + return 'assets/images/icons/armors/armor_01.png'; case EquipmentSlot.accessory: - return 'assets/data/icon/icon_accessory.png'; + return 'assets/images/icons/accessories/ring_02.png'; case EquipmentSlot.consumable: - return 'assets/data/icon/icon_accessory.png'; // Todo: Add potion icon + return 'assets/images/icons/potions/potion_blue.png'; } } } diff --git a/lib/widgets/battle/battle_controls.dart b/lib/widgets/battle/battle_controls.dart index b021b5f..9a39c78 100644 --- a/lib/widgets/battle/battle_controls.dart +++ b/lib/widgets/battle/battle_controls.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import '../../game/config.dart'; +import '../common/custom_icon_button.dart'; class BattleControls extends StatelessWidget { final bool isAttackEnabled; @@ -18,57 +18,30 @@ class BattleControls extends StatelessWidget { required this.onItemPressed, // New }); - Widget _buildFloatingActionButton({ - required String label, - required Color color, - required String iconPath, // Changed from ActionType to String - required bool isEnabled, - required VoidCallback onPressed, - }) { - return FloatingActionButton( - heroTag: label, - onPressed: isEnabled ? onPressed : null, - backgroundColor: isEnabled ? color : ThemeConfig.btnDisabled, - child: Image.asset( - iconPath, - width: 32, - height: 32, - color: ThemeConfig.textColorWhite, // Tint icon white - fit: BoxFit.contain, - filterQuality: FilterQuality.high, - ), - ); - } - @override Widget build(BuildContext context) { return Column( mainAxisSize: MainAxisSize.min, children: [ - _buildFloatingActionButton( - label: "ATK", - color: ThemeConfig.btnActionActive, - iconPath: 'assets/data/icon/icon_weapon.png', + CustomIconButton( + iconPath: 'assets/images/icons/weapons/sword_02.png', isEnabled: isAttackEnabled, - onPressed: onAttackPressed, + onTap: onAttackPressed, + iconColor: Colors.white, ), const SizedBox(height: 16), - _buildFloatingActionButton( - label: "DEF", - color: ThemeConfig.btnDefendActive, - iconPath: 'assets/data/icon/icon_shield.png', + CustomIconButton( + iconPath: 'assets/images/icons/subweapons/shield_01.png', isEnabled: isDefendEnabled, - onPressed: onDefendPressed, + onTap: onDefendPressed, + iconColor: Colors.white, ), const SizedBox(height: 16), - _buildFloatingActionButton( - label: "ITEM", - color: Colors.indigoAccent, // Distinct color for Item - iconPath: - 'assets/data/icon/icon_accessory.png', // Placeholder for Bag - isEnabled: - isAttackEnabled, // Enabled when it's player turn (same as attack) - onPressed: onItemPressed, + CustomIconButton( + iconPath: 'assets/images/icons/potions/potion_blue.png', + isEnabled: isAttackEnabled, // Enabled when it's player turn + onTap: onItemPressed, + iconColor: Colors.white, ), ], ); diff --git a/lib/widgets/common/item_card_widget.dart b/lib/widgets/common/item_card_widget.dart index 33eee6e..b4dc476 100644 --- a/lib/widgets/common/item_card_widget.dart +++ b/lib/widgets/common/item_card_widget.dart @@ -24,7 +24,7 @@ class ItemCardWidget extends StatelessWidget { onTap: onTap, child: Card( color: ThemeConfig.shopItemCardBg, // Configurable if needed - shape: item.rarity != ItemRarity.magic + shape: item.rarity != ItemRarity.normal ? RoundedRectangleBorder( side: BorderSide( color: ItemUtils.getRarityColor(item.rarity), @@ -33,44 +33,66 @@ class ItemCardWidget extends StatelessWidget { borderRadius: BorderRadius.circular(8.0), ) : null, - child: Padding( - padding: const EdgeInsets.all(4.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Expanded( - child: Image.asset( - ItemUtils.getIconPath(item.slot), - fit: BoxFit.contain, + child: Stack( + fit: StackFit.expand, + children: [ + // Background Watermark/Silhouette Icon (Top-Left) + Positioned( + left: 8, + top: 8, + child: Image.asset( + ItemUtils.getIconPath(item.slot), + width: 32, + height: 32, + fit: BoxFit.contain, + color: Colors.black12, // Shadow silhouette + ), + ), + // Main Content (Centered) + Center( + child: Padding( + padding: const EdgeInsets.all(4.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox(height: 12), + Text( + item.name, + maxLines: 1, + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontWeight: FontWeight.bold, + color: ItemUtils.getRarityColor(item.rarity), + fontSize: 12, + ), + ), + const SizedBox(height: 4), + // Show Item Stats + FittedBox( + fit: BoxFit.scaleDown, + child: _buildItemStatText(item), + ), + if (showPrice) ...[ + const SizedBox(height: 4), + Text( + "${item.price} G", + textAlign: TextAlign.center, + style: TextStyle( + color: canBuy + ? ThemeConfig.statGoldColor + : ThemeConfig.textColorGrey, + fontWeight: FontWeight.bold, + fontSize: 12, + ), + ), + ], + ], ), ), - Text( - item.name, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontWeight: FontWeight.bold, - color: ItemUtils.getRarityColor(item.rarity), - fontSize: 12, - ), - ), - // Show Item Stats (Fixed to show negative values) - FittedBox(fit: BoxFit.scaleDown, child: _buildItemStatText(item)), - if (showPrice) ...[ - const Spacer(), - Text( - "${item.price} G", - style: TextStyle( - color: canBuy - ? ThemeConfig.statGoldColor - : ThemeConfig.textColorGrey, - fontWeight: FontWeight.bold, - fontSize: 12, - ), - ), - ], - ], - ), + ), + ], ), ), ); diff --git a/pubspec.yaml b/pubspec.yaml index ebd7a15..d06d8f1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -30,3 +30,9 @@ flutter: - assets/images/enemies/ - assets/images/background/ - assets/images/character/ + - assets/images/icons/borders/ + - assets/images/icons/weapons/ + - assets/images/icons/subweapons/ + - assets/images/icons/accessories/ + - assets/images/icons/potions/ + - assets/images/icons/armors/