This commit is contained in:
2025-12-02 17:28:16 +09:00
parent b4c17a3a00
commit 457eed4d3e
14 changed files with 740 additions and 191 deletions
+169 -30
View File
@@ -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();
}
}