update
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/services.dart';
|
||||
import '../model/entity.dart';
|
||||
|
||||
class EnemyTemplate {
|
||||
final String name;
|
||||
final int baseHp;
|
||||
final int baseAtk;
|
||||
final int baseDefense;
|
||||
|
||||
const EnemyTemplate({
|
||||
required this.name,
|
||||
required this.baseHp,
|
||||
required this.baseAtk,
|
||||
required this.baseDefense,
|
||||
});
|
||||
|
||||
factory EnemyTemplate.fromJson(Map<String, dynamic> json) {
|
||||
return EnemyTemplate(
|
||||
name: json['name'],
|
||||
baseHp: json['baseHp'] ?? 10,
|
||||
baseAtk: json['baseAtk'] ?? 1,
|
||||
baseDefense: json['baseDefense'] ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
Character createCharacter({int stage = 1}) {
|
||||
// Simple additive scaling
|
||||
int scaledHp = baseHp + (stage - 1) * 5;
|
||||
int scaledAtk = baseAtk + (stage - 1);
|
||||
int scaledDefense = baseDefense + (stage ~/ 5); // +1 defense every 5 stages
|
||||
|
||||
return Character(
|
||||
name: name,
|
||||
maxHp: scaledHp,
|
||||
atk: scaledAtk,
|
||||
baseDefense: scaledDefense,
|
||||
armor: 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class EnemyTable {
|
||||
static List<EnemyTemplate> normalEnemies = [];
|
||||
static List<EnemyTemplate> eliteEnemies = [];
|
||||
|
||||
static Future<void> load() async {
|
||||
final String jsonString = await rootBundle.loadString(
|
||||
'assets/data/enemies.json',
|
||||
);
|
||||
final Map<String, dynamic> data = jsonDecode(jsonString);
|
||||
|
||||
normalEnemies = (data['normal'] as List)
|
||||
.map((e) => EnemyTemplate.fromJson(e))
|
||||
.toList();
|
||||
eliteEnemies = (data['elite'] as List)
|
||||
.map((e) => EnemyTemplate.fromJson(e))
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
+32
-151
@@ -1,5 +1,7 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/services.dart';
|
||||
import '../model/item.dart';
|
||||
import '../model/status_effect.dart'; // Import StatusEffect for ItemEffect
|
||||
import '../model/status_effect.dart';
|
||||
|
||||
class ItemTemplate {
|
||||
final String name;
|
||||
@@ -8,7 +10,7 @@ class ItemTemplate {
|
||||
final int baseHp;
|
||||
final int baseArmor;
|
||||
final EquipmentSlot slot;
|
||||
final List<ItemEffect> effects; // New: Effects this item can inflict
|
||||
final List<ItemEffect> effects;
|
||||
|
||||
const ItemTemplate({
|
||||
required this.name,
|
||||
@@ -17,9 +19,24 @@ class ItemTemplate {
|
||||
this.baseHp = 0,
|
||||
this.baseArmor = 0,
|
||||
required this.slot,
|
||||
this.effects = const [], // Default to no effects
|
||||
this.effects = const [],
|
||||
});
|
||||
|
||||
factory ItemTemplate.fromJson(Map<String, dynamic> json) {
|
||||
return ItemTemplate(
|
||||
name: json['name'],
|
||||
description: json['description'],
|
||||
baseAtk: json['baseAtk'] ?? 0,
|
||||
baseHp: json['baseHp'] ?? 0,
|
||||
baseArmor: json['baseArmor'] ?? 0,
|
||||
slot: EquipmentSlot.values.firstWhere((e) => e.name == json['slot']),
|
||||
effects: (json['effects'] as List<dynamic>?)
|
||||
?.map((e) => ItemEffect.fromJson(e))
|
||||
.toList() ??
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
// Create an instance of Item based on this template, optionally scaling with stage
|
||||
Item createItem({int stage = 1}) {
|
||||
// Simple scaling logic: add stage-1 to relevant stats
|
||||
@@ -49,156 +66,20 @@ class ItemTemplate {
|
||||
}
|
||||
|
||||
class ItemTable {
|
||||
static final List<ItemTemplate> weapons = [
|
||||
const ItemTemplate(
|
||||
name: "Rusty Dagger",
|
||||
description: "Old and rusty, but better than nothing.",
|
||||
baseAtk: 3,
|
||||
slot: EquipmentSlot.weapon,
|
||||
),
|
||||
const ItemTemplate(
|
||||
name: "Iron Sword",
|
||||
description: "A standard soldier's sword.",
|
||||
baseAtk: 8,
|
||||
slot: EquipmentSlot.weapon,
|
||||
),
|
||||
const ItemTemplate(
|
||||
name: "Battle Axe",
|
||||
description: "Heavy but powerful.",
|
||||
baseAtk: 12,
|
||||
slot: EquipmentSlot.weapon,
|
||||
),
|
||||
// New: Weapons with status effects
|
||||
ItemTemplate(
|
||||
name: "Stunning Hammer",
|
||||
description: "A heavy hammer that can stun foes.",
|
||||
baseAtk: 10,
|
||||
slot: EquipmentSlot.weapon,
|
||||
effects: [
|
||||
ItemEffect(
|
||||
type: StatusEffectType.stun,
|
||||
probability: 20,
|
||||
duration: 1,
|
||||
), // 20% chance to stun for 1 turn
|
||||
],
|
||||
),
|
||||
ItemTemplate(
|
||||
name: "Jagged Dagger",
|
||||
description: "A cruel dagger that causes bleeding.",
|
||||
baseAtk: 7,
|
||||
slot: EquipmentSlot.weapon,
|
||||
effects: [
|
||||
ItemEffect(
|
||||
type: StatusEffectType.bleed,
|
||||
probability: 30,
|
||||
duration: 3,
|
||||
value: 5,
|
||||
), // 30% chance to bleed (5 dmg/turn for 3 turns)
|
||||
],
|
||||
),
|
||||
ItemTemplate(
|
||||
name: "Sunderer Axe",
|
||||
description: "An axe that exposes enemy weaknesses.",
|
||||
baseAtk: 11,
|
||||
slot: EquipmentSlot.weapon,
|
||||
effects: [
|
||||
ItemEffect(
|
||||
type: StatusEffectType.vulnerable,
|
||||
probability: 100,
|
||||
duration: 2,
|
||||
), // 100% chance to make vulnerable for 2 turns
|
||||
],
|
||||
),
|
||||
];
|
||||
static List<ItemTemplate> weapons = [];
|
||||
static List<ItemTemplate> armors = [];
|
||||
static List<ItemTemplate> shields = [];
|
||||
static List<ItemTemplate> accessories = [];
|
||||
|
||||
static const List<ItemTemplate> armors = [
|
||||
ItemTemplate(
|
||||
name: "Torn Tunic",
|
||||
description: "Offers minimal protection.",
|
||||
baseHp: 10,
|
||||
slot: EquipmentSlot.armor,
|
||||
),
|
||||
ItemTemplate(
|
||||
name: "Leather Vest",
|
||||
description: "Light and flexible.",
|
||||
baseHp: 30,
|
||||
slot: EquipmentSlot.armor,
|
||||
),
|
||||
ItemTemplate(
|
||||
name: "Chainmail",
|
||||
description: "Reliable protection against cuts.",
|
||||
baseHp: 60,
|
||||
slot: EquipmentSlot.armor,
|
||||
),
|
||||
];
|
||||
static Future<void> load() async {
|
||||
final String jsonString = await rootBundle.loadString('assets/data/items.json');
|
||||
final Map<String, dynamic> data = jsonDecode(jsonString);
|
||||
|
||||
static final List<ItemTemplate> shields = [
|
||||
const ItemTemplate(
|
||||
name: "Pot Lid",
|
||||
description: "It was used for cooking.",
|
||||
baseArmor: 1,
|
||||
slot: EquipmentSlot.shield,
|
||||
),
|
||||
const ItemTemplate(
|
||||
name: "Wooden Shield",
|
||||
description: "Sturdy oak wood.",
|
||||
baseArmor: 3,
|
||||
slot: EquipmentSlot.shield,
|
||||
),
|
||||
const ItemTemplate(
|
||||
name: "Kite Shield",
|
||||
description: "Used by knights.",
|
||||
baseArmor: 6,
|
||||
slot: EquipmentSlot.shield,
|
||||
),
|
||||
// New: Shield with Defense Forbidden effect (example)
|
||||
ItemTemplate(
|
||||
name: "Cursed Shield",
|
||||
description:
|
||||
"A shield that prevents the wielder from defending themselves.",
|
||||
baseArmor: 5,
|
||||
slot: EquipmentSlot.shield,
|
||||
effects: [
|
||||
ItemEffect(
|
||||
type: StatusEffectType.defenseForbidden,
|
||||
probability: 100,
|
||||
duration: 999,
|
||||
), // Always prevent defending (long duration for testing)
|
||||
],
|
||||
),
|
||||
];
|
||||
|
||||
static const List<ItemTemplate> accessories = [
|
||||
ItemTemplate(
|
||||
name: "Old Ring",
|
||||
description: "A tarnished ring.",
|
||||
baseAtk: 1,
|
||||
baseHp: 5,
|
||||
slot: EquipmentSlot.accessory,
|
||||
),
|
||||
ItemTemplate(
|
||||
name: "Copper Ring",
|
||||
description: "A simple ring",
|
||||
baseAtk: 1,
|
||||
baseHp: 5,
|
||||
slot: EquipmentSlot.accessory,
|
||||
),
|
||||
ItemTemplate(
|
||||
name: "Ruby Amulet",
|
||||
description: "Glows with a faint red light.",
|
||||
baseAtk: 3,
|
||||
baseHp: 15,
|
||||
slot: EquipmentSlot.accessory,
|
||||
),
|
||||
ItemTemplate(
|
||||
name: "Hero's Badge",
|
||||
description: "A badge of honor.",
|
||||
baseAtk: 5,
|
||||
baseHp: 25,
|
||||
baseArmor: 1,
|
||||
slot: EquipmentSlot.accessory,
|
||||
),
|
||||
];
|
||||
weapons = (data['weapons'] as List).map((e) => ItemTemplate.fromJson(e)).toList();
|
||||
armors = (data['armors'] as List).map((e) => ItemTemplate.fromJson(e)).toList();
|
||||
shields = (data['shields'] as List).map((e) => ItemTemplate.fromJson(e)).toList();
|
||||
accessories = (data['accessories'] as List).map((e) => ItemTemplate.fromJson(e)).toList();
|
||||
}
|
||||
|
||||
static List<ItemTemplate> get allItems => [
|
||||
...weapons,
|
||||
|
||||
@@ -16,14 +16,23 @@ class ItemEffect {
|
||||
this.value = 0,
|
||||
});
|
||||
|
||||
factory ItemEffect.fromJson(Map<String, dynamic> json) {
|
||||
return ItemEffect(
|
||||
type: StatusEffectType.values.firstWhere((e) => e.name == json['type']),
|
||||
probability: json['probability'] ?? 0,
|
||||
duration: json['duration'] ?? 0,
|
||||
value: json['value'] ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
String get description {
|
||||
String typeStr = type.name.toUpperCase();
|
||||
// Customize names if needed
|
||||
if (type == StatusEffectType.defenseForbidden) typeStr = "UNBLOCKABLE";
|
||||
|
||||
|
||||
String durationStr = "${duration}t";
|
||||
String valStr = value > 0 ? " ($value dmg)" : "";
|
||||
|
||||
|
||||
return "$typeStr ${probability}% ($durationStr)$valStr";
|
||||
}
|
||||
}
|
||||
|
||||
+8
-5
@@ -1,9 +1,14 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'game/data/item_table.dart';
|
||||
import 'game/data/enemy_table.dart';
|
||||
import 'providers/battle_provider.dart';
|
||||
import 'screens/main_menu_screen.dart';
|
||||
|
||||
void main() {
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await ItemTable.load();
|
||||
await EnemyTable.load();
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
@@ -13,9 +18,7 @@ class MyApp extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MultiProvider(
|
||||
providers: [
|
||||
ChangeNotifierProvider(create: (_) => BattleProvider()),
|
||||
],
|
||||
providers: [ChangeNotifierProvider(create: (_) => BattleProvider())],
|
||||
child: MaterialApp(
|
||||
title: "Colosseum's Choice",
|
||||
theme: ThemeData.dark(),
|
||||
@@ -23,4 +26,4 @@ class MyApp extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,6 +373,58 @@ class _BattleScreenState extends State<BattleScreen> {
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
if (isEnemy)
|
||||
Consumer<BattleProvider>(
|
||||
builder: (context, provider, child) {
|
||||
if (provider.currentEnemyIntent != null && !character.isDead) {
|
||||
final intent = provider.currentEnemyIntent!;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black54,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: Colors.redAccent),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
"INTENT",
|
||||
style: TextStyle(
|
||||
color: Colors.redAccent,
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
intent.type == EnemyActionType.attack
|
||||
? Icons.flash_on
|
||||
: Icons.shield,
|
||||
color: Colors.yellow,
|
||||
size: 16,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
intent.description,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
),
|
||||
if (!isEnemy) ...[
|
||||
Text("Armor: ${character.armor}"),
|
||||
Text("ATK: ${character.totalAtk}"),
|
||||
|
||||
Reference in New Issue
Block a user