This commit is contained in:
2025-12-03 01:39:14 +09:00
parent 0e96aa4f7c
commit 36f93ccbcc
16 changed files with 691 additions and 89 deletions
+155 -46
View File
@@ -1,19 +1,17 @@
import 'dart:async'; // StreamController 사용을 위해 import
import 'dart:math';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import '../game/model/entity.dart';
import '../game/model/item.dart';
import '../game/model/status_effect.dart';
import '../game/model/stage.dart'; // Import StageModel
import '../game/model/stage.dart';
import '../game/data/item_table.dart';
import '../game/data/enemy_table.dart';
import '../utils/game_math.dart';
enum ActionType { attack, defend }
enum RiskLevel { safe, normal, risky }
enum EnemyActionType { attack, defend }
import '../game/enums.dart';
import '../game/model/damage_event.dart'; // DamageEvent import
import '../game/model/effect_event.dart'; // EffectEvent import
class EnemyIntent {
final EnemyActionType type;
@@ -43,10 +41,25 @@ class BattleProvider with ChangeNotifier {
List<Item> rewardOptions = [];
bool showRewardPopup = false;
// Damage Event Stream
final _damageEventController = StreamController<DamageEvent>.broadcast();
Stream<DamageEvent> get damageStream => _damageEventController.stream;
// Effect Event Stream
final _effectEventController = StreamController<EffectEvent>.broadcast();
Stream<EffectEvent> get effectStream => _effectEventController.stream;
BattleProvider() {
// initializeBattle(); // Do not auto-start logic
}
@override
void dispose() {
_damageEventController.close(); // StreamController 닫기
_effectEventController.close();
super.dispose();
}
void initializeBattle() {
stage = 1;
player = Character(
@@ -254,20 +267,61 @@ class BattleProvider with ChangeNotifier {
break;
}
if (success) {
if (type == ActionType.attack) {
int damage = (player.totalAtk * efficiency).toInt();
_applyDamage(enemy, damage);
_addLog("Player dealt $damage damage to Enemy.");
if (success) {
// Try applying status effects from items
_tryApplyStatusEffects(player, enemy);
} else {
int armorGained = (player.totalDefense * efficiency).toInt();
player.armor += armorGained;
_addLog("Player gained $armorGained armor.");
}
} else {
if (type == ActionType.attack) {
int damage = (player.totalAtk * efficiency).toInt();
_effectEventController.sink.add(EffectEvent(
type: ActionType.attack,
risk: risk,
target: EffectTarget.enemy,
));
_applyDamage(enemy, damage, targetType: DamageTarget.enemy); // Add targetType
_addLog("Player dealt $damage damage to Enemy.");
// Try applying status effects from items
_tryApplyStatusEffects(player, enemy);
} else {
_effectEventController.sink.add(EffectEvent(
type: ActionType.defend,
risk: risk,
target: EffectTarget.player,
));
int armorGained = (player.totalDefense * efficiency).toInt();
player.armor += armorGained;
_addLog("Player gained $armorGained armor.");
}
}
else {
_addLog("Player's action missed!");
}
@@ -305,28 +359,38 @@ class BattleProvider with ChangeNotifier {
if (enemy.isDead) {
_onVictory();
return;
// return; // Already handled by _processStartTurnEffects if damage applied
}
if (canAct && currentEnemyIntent != null) {
final intent = currentEnemyIntent!;
// Check Success Rate based on Risk
final random = Random();
bool success = false;
switch (intent.risk) {
case RiskLevel.safe:
success = random.nextDouble() < 1.0;
break;
case RiskLevel.normal:
success = random.nextDouble() < 0.8;
break;
case RiskLevel.risky:
success = random.nextDouble() < 0.4;
break;
}
if (intent.type == EnemyActionType.defend) {
// Already handled in _generateEnemyIntent
_addLog("Enemy maintains defensive stance.");
} else {
// Attack Logic
final random = Random();
bool success = false;
switch (intent.risk) {
case RiskLevel.safe:
success = random.nextDouble() < 1.0;
break;
case RiskLevel.normal:
success = random.nextDouble() < 0.8;
break;
case RiskLevel.risky:
success = random.nextDouble() < 0.4;
break;
}
if (success) {
_effectEventController.sink.add(EffectEvent(
type: ActionType.attack,
risk: intent.risk,
target: EffectTarget.player,
));
if (success) {
if (intent.type == EnemyActionType.attack) {
int incomingDamage = intent.value;
int damageToHp = 0;
@@ -346,16 +410,12 @@ class BattleProvider with ChangeNotifier {
}
if (damageToHp > 0) {
_applyDamage(player, damageToHp);
_applyDamage(player, damageToHp, targetType: DamageTarget.player);
_addLog("Enemy dealt $damageToHp damage to Player HP.");
}
} else if (intent.type == EnemyActionType.defend) {
int armorGained = intent.value;
enemy.armor += armorGained;
_addLog("Enemy gained $armorGained armor.");
} else {
_addLog("Enemy's ${intent.risk.name} attack missed!");
}
} else {
_addLog("Enemy's ${intent.risk.name} action missed!");
}
} else if (!canAct) {
_addLog("Enemy is stunned and cannot act!");
@@ -394,9 +454,25 @@ class BattleProvider with ChangeNotifier {
.toList();
if (bleedEffects.isNotEmpty) {
int totalBleed = bleedEffects.fold(0, (sum, e) => sum + e.value);
int previousHp = character.hp; // Record HP before damage
character.hp -= totalBleed;
if (character.hp < 0) character.hp = 0;
_addLog("${character.name} takes $totalBleed bleed damage!");
// Emit DamageEvent for bleed
if (character == player) {
_damageEventController.sink.add(DamageEvent(
damage: totalBleed,
target: DamageTarget.player,
color: Colors.purpleAccent, // Bleed damage color
));
} else if (character == enemy) {
_damageEventController.sink.add(DamageEvent(
damage: totalBleed,
target: DamageTarget.enemy,
color: Colors.purpleAccent, // Bleed damage color
));
}
}
// 2. Stun Check
@@ -429,15 +505,22 @@ class BattleProvider with ChangeNotifier {
}
}
void _applyDamage(Character target, int damage) {
void _applyDamage(Character target, int damage, {required DamageTarget targetType, Color color = Colors.red}) {
// Check Vulnerable
if (target.hasStatus(StatusEffectType.vulnerable)) {
damage = (damage * 1.5).toInt();
_addLog("Vulnerable! Damage increased to $damage.");
color = Colors.orange; // Vulnerable damage color
}
target.hp -= damage;
if (target.hp < 0) target.hp = 0;
_damageEventController.sink.add(DamageEvent(
damage: damage,
target: targetType,
color: color,
));
}
void _addLog(String message) {
@@ -589,7 +672,33 @@ class BattleProvider with ChangeNotifier {
risk: risk,
description: "Defends for $armor (${risk.name})",
);
// [Changed] Apply defense immediately for pre-emptive defense
bool success = false;
switch (risk) {
case RiskLevel.safe:
success = random.nextDouble() < 1.0;
break;
case RiskLevel.normal:
success = random.nextDouble() < 0.8;
break;
case RiskLevel.risky:
success = random.nextDouble() < 0.4;
break;
}
if (success) {
enemy.armor += armor;
_addLog("Enemy prepares defense! (+$armor Armor)");
_effectEventController.sink.add(EffectEvent(
type: ActionType.defend,
risk: risk,
target: EffectTarget.enemy,
));
} else {
_addLog("Enemy tried to defend but fumbled!");
}
}
notifyListeners();
}
}
}