update
This commit is contained in:
@@ -6,17 +6,35 @@ import '../game/model/item.dart';
|
||||
import '../game/model/status_effect.dart';
|
||||
import '../game/model/stage.dart'; // Import StageModel
|
||||
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 }
|
||||
|
||||
class EnemyIntent {
|
||||
final EnemyActionType type;
|
||||
final int value;
|
||||
final RiskLevel risk;
|
||||
final String description;
|
||||
|
||||
EnemyIntent({
|
||||
required this.type,
|
||||
required this.value,
|
||||
required this.risk,
|
||||
required this.description,
|
||||
});
|
||||
}
|
||||
|
||||
class BattleProvider with ChangeNotifier {
|
||||
late Character player;
|
||||
late Character enemy; // Kept for compatibility, active during Battle/Elite
|
||||
|
||||
late StageModel currentStage; // The current stage object
|
||||
EnemyIntent? currentEnemyIntent;
|
||||
|
||||
List<String> battleLogs = [];
|
||||
bool isPlayerTurn = true;
|
||||
@@ -114,20 +132,46 @@ class BattleProvider with ChangeNotifier {
|
||||
|
||||
if (type == StageType.battle || type == StageType.elite) {
|
||||
bool isElite = type == StageType.elite;
|
||||
int hpMultiplier = isElite ? 1 : 1;
|
||||
int atkMultiplier = isElite ? 4 : 2;
|
||||
// Select random enemy template
|
||||
final random = Random();
|
||||
EnemyTemplate template;
|
||||
if (isElite) {
|
||||
if (EnemyTable.eliteEnemies.isNotEmpty) {
|
||||
template = EnemyTable
|
||||
.eliteEnemies[random.nextInt(EnemyTable.eliteEnemies.length)];
|
||||
} else {
|
||||
// Fallback if no elite enemies loaded
|
||||
template = const EnemyTemplate(
|
||||
name: "Elite Guardian",
|
||||
baseHp: 50,
|
||||
baseAtk: 10,
|
||||
baseDefense: 2,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (EnemyTable.normalEnemies.isNotEmpty) {
|
||||
template = EnemyTable
|
||||
.normalEnemies[random.nextInt(EnemyTable.normalEnemies.length)];
|
||||
} else {
|
||||
// Fallback
|
||||
template = const EnemyTemplate(
|
||||
name: "Enemy",
|
||||
baseHp: 20,
|
||||
baseAtk: 5,
|
||||
baseDefense: 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
int enemyHp = 1 + (stage - 1) * hpMultiplier;
|
||||
int enemyAtk = 8 + (stage - 1) * atkMultiplier;
|
||||
|
||||
String name = isElite ? "Elite Guardian" : "Enemy";
|
||||
newEnemy = Character(name: name, maxHp: enemyHp, armor: 0, atk: enemyAtk);
|
||||
newEnemy = template.createCharacter(stage: stage);
|
||||
|
||||
// Assign to the main 'enemy' field for UI compatibility
|
||||
enemy = newEnemy;
|
||||
isPlayerTurn = true;
|
||||
showRewardPopup = false;
|
||||
|
||||
_generateEnemyIntent(); // Generate first intent
|
||||
|
||||
_addLog("Stage $stage ($type) started! A wild ${enemy.name} appeared.");
|
||||
} else if (type == StageType.shop) {
|
||||
// Generate random items for shop
|
||||
@@ -263,35 +307,65 @@ class BattleProvider with ChangeNotifier {
|
||||
return;
|
||||
}
|
||||
|
||||
if (canAct) {
|
||||
int incomingDamage = enemy.totalAtk;
|
||||
int damageToHp = 0;
|
||||
if (canAct && currentEnemyIntent != null) {
|
||||
final intent = currentEnemyIntent!;
|
||||
|
||||
// Enemy attack logic
|
||||
// (Simple logic: Enemy always attacks for now)
|
||||
// Note: Enemy doesn't have equipment yet, so no effects applied by enemy.
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Handle Player Armor
|
||||
if (player.armor > 0) {
|
||||
if (player.armor >= incomingDamage) {
|
||||
player.armor -= incomingDamage;
|
||||
damageToHp = 0;
|
||||
_addLog("Armor absorbed all $incomingDamage damage.");
|
||||
} else {
|
||||
damageToHp = incomingDamage - player.armor;
|
||||
_addLog("Armor absorbed ${player.armor} damage.");
|
||||
player.armor = 0;
|
||||
if (success) {
|
||||
if (intent.type == EnemyActionType.attack) {
|
||||
int incomingDamage = intent.value;
|
||||
int damageToHp = 0;
|
||||
|
||||
// Handle Player Armor
|
||||
if (player.armor > 0) {
|
||||
if (player.armor >= incomingDamage) {
|
||||
player.armor -= incomingDamage;
|
||||
damageToHp = 0;
|
||||
_addLog("Armor absorbed all $incomingDamage damage.");
|
||||
} else {
|
||||
damageToHp = incomingDamage - player.armor;
|
||||
_addLog("Armor absorbed ${player.armor} damage.");
|
||||
player.armor = 0;
|
||||
}
|
||||
} else {
|
||||
damageToHp = incomingDamage;
|
||||
}
|
||||
|
||||
if (damageToHp > 0) {
|
||||
_applyDamage(player, damageToHp);
|
||||
_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 {
|
||||
damageToHp = incomingDamage;
|
||||
_addLog("Enemy's ${intent.risk.name} action missed!");
|
||||
}
|
||||
|
||||
if (damageToHp > 0) {
|
||||
_applyDamage(player, damageToHp);
|
||||
_addLog("Enemy dealt $damageToHp damage to Player HP.");
|
||||
}
|
||||
} else {
|
||||
} else if (!canAct) {
|
||||
_addLog("Enemy is stunned and cannot act!");
|
||||
} else {
|
||||
_addLog("Enemy did nothing.");
|
||||
}
|
||||
|
||||
// Generate next intent
|
||||
if (!player.isDead) {
|
||||
_generateEnemyIntent();
|
||||
}
|
||||
|
||||
// Player Turn Start Logic
|
||||
@@ -452,4 +526,69 @@ class BattleProvider with ChangeNotifier {
|
||||
stage++;
|
||||
_prepareNextStage();
|
||||
}
|
||||
|
||||
void _generateEnemyIntent() {
|
||||
if (enemy.isDead) {
|
||||
currentEnemyIntent = null;
|
||||
return;
|
||||
}
|
||||
|
||||
final random = Random();
|
||||
|
||||
// Decide Action Type
|
||||
// If baseDefense is 0, CANNOT defend.
|
||||
bool canDefend = enemy.baseDefense > 0;
|
||||
bool isAttack = true;
|
||||
|
||||
if (canDefend) {
|
||||
// 70% Attack, 30% Defend
|
||||
isAttack = random.nextDouble() < 0.7;
|
||||
} else {
|
||||
isAttack = true;
|
||||
}
|
||||
|
||||
// Decide Risk Level
|
||||
RiskLevel risk = RiskLevel.values[random.nextInt(RiskLevel.values.length)];
|
||||
double efficiency = 1.0;
|
||||
switch (risk) {
|
||||
case RiskLevel.safe:
|
||||
efficiency = 0.5;
|
||||
break;
|
||||
case RiskLevel.normal:
|
||||
efficiency = 1.0;
|
||||
break;
|
||||
case RiskLevel.risky:
|
||||
efficiency = 2.0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (isAttack) {
|
||||
// Attack Intent
|
||||
// Variance: +/- 20%
|
||||
double variance = 0.8 + random.nextDouble() * 0.4;
|
||||
int damage = (enemy.totalAtk * efficiency * variance).toInt();
|
||||
if (damage < 1) damage = 1;
|
||||
|
||||
currentEnemyIntent = EnemyIntent(
|
||||
type: EnemyActionType.attack,
|
||||
value: damage,
|
||||
risk: risk,
|
||||
description: "Attacks for $damage (${risk.name})",
|
||||
);
|
||||
} else {
|
||||
// Defend Intent
|
||||
int baseDef = enemy.totalDefense;
|
||||
// Variance
|
||||
double variance = 0.8 + random.nextDouble() * 0.4;
|
||||
int armor = (baseDef * 2 * efficiency * variance).toInt();
|
||||
|
||||
currentEnemyIntent = EnemyIntent(
|
||||
type: EnemyActionType.defend,
|
||||
value: armor,
|
||||
risk: risk,
|
||||
description: "Defends for $armor (${risk.name})",
|
||||
);
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user