217 lines
6.6 KiB
Dart
217 lines
6.6 KiB
Dart
import 'dart:convert';
|
|
import 'dart:math';
|
|
import 'package:flutter/services.dart';
|
|
import '../model/item.dart';
|
|
import '../enums.dart';
|
|
import '../config/item_config.dart';
|
|
// import 'item_prefix_table.dart'; // Logic moved to LootGenerator
|
|
// import 'name_generator.dart'; // Logic moved to LootGenerator
|
|
import '../logic/loot_generator.dart'; // Import LootGenerator
|
|
|
|
class ItemTemplate {
|
|
final String id;
|
|
final String name;
|
|
final String description;
|
|
final int atkBonus;
|
|
final int hpBonus;
|
|
final int armorBonus;
|
|
final EquipmentSlot slot;
|
|
final List<ItemEffect> effects;
|
|
final int price;
|
|
final String? image;
|
|
final int luck;
|
|
final ItemRarity rarity;
|
|
final ItemTier tier;
|
|
|
|
const ItemTemplate({
|
|
required this.id,
|
|
required this.name,
|
|
required this.description,
|
|
required this.atkBonus,
|
|
required this.hpBonus,
|
|
required this.armorBonus,
|
|
required this.slot,
|
|
required this.effects,
|
|
required this.price,
|
|
this.image,
|
|
this.luck = 0,
|
|
this.rarity = ItemRarity.magic,
|
|
this.tier = ItemTier.tier1,
|
|
});
|
|
|
|
factory ItemTemplate.fromJson(Map<String, dynamic> json) {
|
|
var effectsList = <ItemEffect>[];
|
|
if (json['effects'] != null) {
|
|
effectsList = (json['effects'] as List)
|
|
.map((e) => ItemEffect.fromJson(e))
|
|
.toList();
|
|
}
|
|
|
|
return ItemTemplate(
|
|
id: json['id'],
|
|
name: json['name'],
|
|
description: json['description'],
|
|
atkBonus: json['atkBonus'] ?? json['baseAtk'] ?? 0,
|
|
hpBonus: json['hpBonus'] ?? json['baseHp'] ?? 0,
|
|
armorBonus: json['armorBonus'] ?? json['baseArmor'] ?? 0,
|
|
slot: EquipmentSlot.values.firstWhere((e) => e.name == json['slot']),
|
|
effects: effectsList,
|
|
price: json['price'] ?? 10,
|
|
image: json['image'],
|
|
luck: json['luck'] ?? 0,
|
|
rarity: json['rarity'] != null
|
|
? ItemRarity.values.firstWhere((e) => e.name == json['rarity'])
|
|
: ItemRarity.magic,
|
|
tier: json['tier'] != null
|
|
? ItemTier.values.firstWhere((e) => e.name == json['tier'])
|
|
: ItemTier.tier1,
|
|
);
|
|
}
|
|
|
|
Item createItem({int stage = 1}) {
|
|
// Stage parameter kept for interface compatibility but unused here,
|
|
// as scaling is now handled via Tier/Rarity in LootGenerator/Table logic.
|
|
return LootGenerator.generate(this);
|
|
}
|
|
}
|
|
|
|
class ItemTable {
|
|
static List<ItemTemplate> weapons = [];
|
|
static List<ItemTemplate> armors = [];
|
|
static List<ItemTemplate> shields = [];
|
|
static List<ItemTemplate> accessories = [];
|
|
|
|
static Future<void> load() async {
|
|
final String jsonString = await rootBundle.loadString(
|
|
'assets/data/items.json',
|
|
);
|
|
final Map<String, dynamic> data = jsonDecode(jsonString);
|
|
|
|
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,
|
|
...armors,
|
|
...shields,
|
|
...accessories,
|
|
];
|
|
|
|
static ItemTemplate? get(String id) {
|
|
try {
|
|
return allItems.firstWhere((item) => item.id == id);
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
static final Random _random = Random();
|
|
|
|
/// Returns all items matching the given tier.
|
|
static List<ItemTemplate> getItemsByTier(ItemTier tier) {
|
|
return allItems.where((item) => item.tier == tier).toList();
|
|
}
|
|
|
|
/// Returns a random item based on Tier and Rarity weights.
|
|
///
|
|
/// [tier]: The tier of items to select from.
|
|
/// [slot]: Optional. If provided, only items of this slot are considered.
|
|
/// [weights]: Optional map of rarity weights. Key: Rarity, Value: Weight.
|
|
/// [minRarity]: Optional. Minimum rarity to consider (inclusive).
|
|
/// [maxRarity]: Optional. Maximum rarity to consider (inclusive).
|
|
static ItemTemplate? getRandomItem({
|
|
required ItemTier tier,
|
|
EquipmentSlot? slot,
|
|
Map<ItemRarity, int>? weights,
|
|
ItemRarity? minRarity,
|
|
ItemRarity? maxRarity,
|
|
}) {
|
|
// 1. Filter by Tier and Slot (if provided)
|
|
var candidates = allItems.where((item) => item.tier == tier);
|
|
if (slot != null) {
|
|
candidates = candidates.where((item) => item.slot == slot);
|
|
}
|
|
|
|
if (candidates.isEmpty) return null;
|
|
|
|
// 2. Prepare Rarity Weights (Filtered by min/max)
|
|
Map<ItemRarity, int> activeWeights = Map.from(
|
|
weights ?? ItemConfig.defaultRarityWeights,
|
|
);
|
|
|
|
if (minRarity != null) {
|
|
activeWeights.removeWhere((r, w) => r.index < minRarity.index);
|
|
}
|
|
if (maxRarity != null) {
|
|
activeWeights.removeWhere((r, w) => r.index > maxRarity.index);
|
|
}
|
|
|
|
if (activeWeights.isEmpty) {
|
|
// Fallback: If weights eliminated all options (e.g. misconfiguration),
|
|
// try to find ANY item within rarity range from candidates.
|
|
if (minRarity != null) {
|
|
candidates = candidates.where(
|
|
(item) => item.rarity.index >= minRarity.index,
|
|
);
|
|
}
|
|
if (maxRarity != null) {
|
|
candidates = candidates.where(
|
|
(item) => item.rarity.index <= maxRarity.index,
|
|
);
|
|
}
|
|
if (candidates.isEmpty) return null;
|
|
return candidates.toList()[_random.nextInt(candidates.length)];
|
|
}
|
|
|
|
// 3. Determine Target Rarity based on filtered weights
|
|
int totalWeight = activeWeights.values.fold(0, (sum, w) => sum + w);
|
|
int roll = _random.nextInt(totalWeight);
|
|
|
|
ItemRarity? selectedRarity;
|
|
int currentSum = 0;
|
|
|
|
for (var entry in activeWeights.entries) {
|
|
currentSum += entry.value;
|
|
if (roll < currentSum) {
|
|
selectedRarity = entry.key;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 4. Filter candidates by Selected Rarity
|
|
var rarityCandidates = candidates
|
|
.where((item) => item.rarity == selectedRarity)
|
|
.toList();
|
|
|
|
// 5. Fallback: If no items of selected rarity, use any item from the filtered candidates (respecting min/max)
|
|
if (rarityCandidates.isEmpty) {
|
|
if (minRarity != null) {
|
|
candidates = candidates.where(
|
|
(item) => item.rarity.index >= minRarity.index,
|
|
);
|
|
}
|
|
if (maxRarity != null) {
|
|
candidates = candidates.where(
|
|
(item) => item.rarity.index <= maxRarity.index,
|
|
);
|
|
}
|
|
if (candidates.isEmpty) return null;
|
|
return candidates.toList()[_random.nextInt(candidates.length)];
|
|
}
|
|
|
|
// 6. Pick random item
|
|
return rarityCandidates[_random.nextInt(rarityCandidates.length)];
|
|
}
|
|
}
|