feat: add subweapon support, weapon type logic, and compact item card UI options
This commit is contained in:
@@ -43,6 +43,9 @@ class EnemyIntent {
|
||||
}
|
||||
|
||||
class BattleProvider with ChangeNotifier {
|
||||
static final Duration _turnEffectVisualDelay =
|
||||
AnimationConfig.floatingTextDuration + const Duration(milliseconds: 100);
|
||||
|
||||
late Character player;
|
||||
late Character enemy; // Kept for compatibility, active during Battle/Elite
|
||||
|
||||
@@ -52,8 +55,6 @@ class BattleProvider with ChangeNotifier {
|
||||
final BattleLogManager _logManager = BattleLogManager();
|
||||
bool isPlayerTurn = true;
|
||||
int _turnTransactionId = 0; // To prevent async race conditions
|
||||
bool skipAnimations = false; // Sync with SettingsProvider
|
||||
|
||||
int stage = 1;
|
||||
int turnCount = 1;
|
||||
List<Item> rewardOptions = [];
|
||||
@@ -62,6 +63,19 @@ class BattleProvider with ChangeNotifier {
|
||||
|
||||
List<String> get logs => _logManager.logs;
|
||||
int get lastGoldReward => _lastGoldReward;
|
||||
StageType get nextStageType => getStageTypeFor(stage + 1);
|
||||
|
||||
StageType getStageTypeFor(int stageNumber) {
|
||||
if (stageNumber % GameConfig.eliteStageInterval == 0) {
|
||||
return StageType.elite;
|
||||
} else if (stageNumber % GameConfig.shopStageInterval == 0) {
|
||||
return StageType.shop;
|
||||
} else if (stageNumber % GameConfig.restStageInterval == 0) {
|
||||
return StageType.rest;
|
||||
}
|
||||
|
||||
return StageType.battle;
|
||||
}
|
||||
|
||||
void refreshUI() {
|
||||
notifyListeners();
|
||||
@@ -75,6 +89,10 @@ class BattleProvider with ChangeNotifier {
|
||||
final _effectEventController = StreamController<EffectEvent>.broadcast();
|
||||
Stream<EffectEvent> get effectStream => _effectEventController.stream;
|
||||
|
||||
// Heal Event Stream
|
||||
final _healEventController = StreamController<HealEvent>.broadcast();
|
||||
Stream<HealEvent> get healStream => _healEventController.stream;
|
||||
|
||||
// Dependency injection
|
||||
final ShopProvider shopProvider;
|
||||
final Random _random; // Injected Random instance
|
||||
@@ -88,6 +106,7 @@ class BattleProvider with ChangeNotifier {
|
||||
void dispose() {
|
||||
_damageEventController.close(); // StreamController 닫기
|
||||
_effectEventController.close();
|
||||
_healEventController.close();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -136,8 +155,8 @@ class BattleProvider with ChangeNotifier {
|
||||
player.gold = GameConfig.startingGold;
|
||||
|
||||
// Add new status effect items for testing
|
||||
player.addToInventory(ItemTable.weapons[3].createItem()); // Stunning Hammer
|
||||
player.addToInventory(ItemTable.weapons[4].createItem()); // Jagged Dagger
|
||||
player.addToInventory(ItemTable.weapons[6].createItem()); // Stunning Hammer
|
||||
player.addToInventory(ItemTable.weapons[9].createItem()); // Jagged Dagger
|
||||
player.addToInventory(ItemTable.weapons[5].createItem()); // Sunderer Axe
|
||||
player.addToInventory(ItemTable.shields[3].createItem()); // Cursed Shield
|
||||
|
||||
@@ -164,18 +183,7 @@ class BattleProvider with ChangeNotifier {
|
||||
// Reset Player Armor at start of new stage
|
||||
player.armor = 0;
|
||||
|
||||
StageType type;
|
||||
|
||||
// Stage Type Logic
|
||||
if (stage % GameConfig.eliteStageInterval == 0) {
|
||||
type = StageType.elite; // Every 10th stage is a Boss/Elite
|
||||
} else if (stage % GameConfig.shopStageInterval == 0) {
|
||||
type = StageType.shop; // Every 5th stage is a Shop (except 10, 20...)
|
||||
} else if (stage % GameConfig.restStageInterval == 0) {
|
||||
type = StageType.rest; // Every 8th stage is a Rest
|
||||
} else {
|
||||
type = StageType.battle;
|
||||
}
|
||||
StageType type = getStageTypeFor(stage);
|
||||
|
||||
// Prepare Data based on Type
|
||||
Character? newEnemy;
|
||||
@@ -239,8 +247,7 @@ class BattleProvider with ChangeNotifier {
|
||||
// 0. Ensure Pre-emptive Enemy Defense is applied (if not already via animation)
|
||||
applyPendingEnemyDefense();
|
||||
|
||||
// Update Enemy Status Effects at the start of Player's turn (user request)
|
||||
enemy.updateStatusEffects(); // 1. Check for Defense Forbidden status
|
||||
// 1. Check for Defense Forbidden status (Player)
|
||||
if (type == ActionType.defend &&
|
||||
player.hasStatus(StatusEffectType.defenseForbidden)) {
|
||||
_addLog("Cannot defend! You are under Defense Forbidden status.");
|
||||
@@ -262,7 +269,7 @@ class BattleProvider with ChangeNotifier {
|
||||
|
||||
// If a visual effect occurred (bleed, stun), wait a bit before action
|
||||
if (turnEffect.effectTriggered) {
|
||||
await Future.delayed(const Duration(milliseconds: 800));
|
||||
await Future.delayed(_turnEffectVisualDelay);
|
||||
}
|
||||
|
||||
if (!turnEffect.canAct) {
|
||||
@@ -390,7 +397,7 @@ class BattleProvider with ChangeNotifier {
|
||||
|
||||
void _endPlayerTurn() {
|
||||
// Update durations at end of turn
|
||||
player.updateStatusEffects();
|
||||
player.updateEndOfTurnStatusEffects();
|
||||
|
||||
// Check if enemy is dead from bleed
|
||||
if (enemy.isDead) {
|
||||
@@ -519,6 +526,9 @@ class BattleProvider with ChangeNotifier {
|
||||
effectTriggered = true;
|
||||
}
|
||||
|
||||
// 3. Update durations for start-of-turn effects immediately
|
||||
character.updateStartOfTurnStatusEffects();
|
||||
|
||||
return TurnEffectResult(
|
||||
canAct: !isStunned,
|
||||
effectTriggered: effectTriggered,
|
||||
@@ -550,7 +560,7 @@ class BattleProvider with ChangeNotifier {
|
||||
|
||||
// If a visual effect occurred (bleed, stun), wait a bit before action
|
||||
if (turnEffect.effectTriggered) {
|
||||
await Future.delayed(const Duration(milliseconds: 800));
|
||||
await Future.delayed(_turnEffectVisualDelay);
|
||||
}
|
||||
|
||||
if (turnEffect.canAct && currentEnemyIntent != null) {
|
||||
@@ -665,7 +675,7 @@ class BattleProvider with ChangeNotifier {
|
||||
if (player.isDead) return; // Game Over check
|
||||
|
||||
// Update enemy status at the end of their turn
|
||||
enemy.updateStatusEffects();
|
||||
enemy.updateEndOfTurnStatusEffects();
|
||||
|
||||
// Generate NEXT intent
|
||||
_generateEnemyIntent();
|
||||
@@ -765,16 +775,20 @@ class BattleProvider with ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool selectReward(Item item) {
|
||||
bool selectReward(Item item, {bool completeStage = true}) {
|
||||
if (item.id == "reward_skip") {
|
||||
_addLog("Skipped reward.");
|
||||
_completeStage();
|
||||
if (completeStage) {
|
||||
_completeStage();
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
bool added = player.addToInventory(item);
|
||||
if (added) {
|
||||
_addLog("Added ${item.name} to inventory.");
|
||||
_completeStage();
|
||||
if (completeStage) {
|
||||
_completeStage();
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
_addLog("Inventory is full! Could not take ${item.name}.");
|
||||
@@ -783,6 +797,10 @@ class BattleProvider with ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
void completeStage() {
|
||||
_completeStage();
|
||||
}
|
||||
|
||||
void _completeStage() {
|
||||
// Heal player after selecting reward
|
||||
int healAmount = GameMath.floor(
|
||||
@@ -802,9 +820,20 @@ class BattleProvider with ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void equipItem(Item item) {
|
||||
if (player.equip(item)) {
|
||||
_addLog("Equipped ${item.name}.");
|
||||
void equipItem(Item item, {EquipmentSlot? targetSlot}) {
|
||||
final success = targetSlot == null
|
||||
? player.equip(item)
|
||||
: player.equipToSlot(item, targetSlot);
|
||||
|
||||
if (success) {
|
||||
final slotName = targetSlot == null
|
||||
? item.typeName
|
||||
: targetSlot == EquipmentSlot.weapon
|
||||
? "Main Weapon"
|
||||
: targetSlot == EquipmentSlot.shield
|
||||
? "Subweapon"
|
||||
: targetSlot.name;
|
||||
_addLog("Equipped ${item.name} as $slotName.");
|
||||
} else {
|
||||
_addLog(
|
||||
"Failed to equip ${item.name}.",
|
||||
@@ -857,6 +886,7 @@ class BattleProvider with ChangeNotifier {
|
||||
int healedAmount = player.hp - currentHp;
|
||||
if (healedAmount > 0) {
|
||||
_addLog("Used ${item.name}. Recovered $healedAmount HP.");
|
||||
_healEventController.sink.add(HealEvent(amount: healedAmount, target: HealTarget.player));
|
||||
effectApplied = true;
|
||||
} else {
|
||||
_addLog("Used ${item.name}. HP is already full.");
|
||||
@@ -1095,6 +1125,7 @@ class BattleProvider with ChangeNotifier {
|
||||
type: target.hasStatus(StatusEffectType.vulnerable)
|
||||
? DamageType.vulnerable
|
||||
: DamageType.normal,
|
||||
risk: event.risk,
|
||||
),
|
||||
);
|
||||
_addLog("${attacker.name} dealt $damageToHp damage to ${target.name}.");
|
||||
|
||||
@@ -17,9 +17,9 @@ class ShopProvider with ChangeNotifier {
|
||||
|
||||
void generateShopItems(int stage) {
|
||||
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;
|
||||
|
||||
availableItems = [];
|
||||
|
||||
Reference in New Issue
Block a user