Refactor Item Creation: Introduce LootGenerator
This commit is contained in:
@@ -4,8 +4,9 @@ import 'package:flutter/services.dart';
|
||||
import '../model/item.dart';
|
||||
import '../enums.dart';
|
||||
import '../config/item_config.dart';
|
||||
import 'item_prefix_table.dart'; // Import prefix table
|
||||
import 'name_generator.dart'; // Import name generator
|
||||
// import 'item_prefix_table.dart'; // Logic moved to LootGenerator
|
||||
// import 'name_generator.dart'; // Logic moved to LootGenerator
|
||||
import '../logic/loot_generator.dart'; // Import LootGenerator
|
||||
import '../../utils/game_math.dart';
|
||||
|
||||
class ItemTemplate {
|
||||
@@ -69,122 +70,9 @@ class ItemTemplate {
|
||||
}
|
||||
|
||||
Item createItem({int stage = 1}) {
|
||||
// Stage-based scaling is removed.
|
||||
// Apply Prefix Logic based on Rarity.
|
||||
|
||||
String finalName = name;
|
||||
int finalAtk = atkBonus;
|
||||
int finalHp = hpBonus;
|
||||
int finalArmor = armorBonus;
|
||||
int finalLuck = luck;
|
||||
|
||||
final random = Random();
|
||||
|
||||
// 0. Normal Rarity: Prefix logic for base stat variations
|
||||
if (rarity == ItemRarity.normal) {
|
||||
// Weighted Random Selection
|
||||
final prefixes = ItemPrefixTable.normalPrefixes;
|
||||
int totalWeight = prefixes.fold(0, (sum, p) => sum + p.weight);
|
||||
int roll = random.nextInt(totalWeight);
|
||||
|
||||
ItemModifier? selectedModifier;
|
||||
int currentSum = 0;
|
||||
for (var mod in prefixes) {
|
||||
currentSum += mod.weight;
|
||||
if (roll < currentSum) {
|
||||
selectedModifier = mod;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedModifier != null) {
|
||||
if (selectedModifier.prefix.isNotEmpty) {
|
||||
finalName = "${selectedModifier.prefix} $name";
|
||||
}
|
||||
|
||||
double mult = selectedModifier.multiplier;
|
||||
if (mult != 1.0) {
|
||||
finalAtk = (finalAtk * mult).floor();
|
||||
finalHp = (finalHp * mult).floor();
|
||||
finalArmor = (finalArmor * mult).floor();
|
||||
// Luck usually isn't scaled by small multipliers, but let's keep it consistent or skip.
|
||||
// Skipping luck scaling for normal prefixes to avoid 0.
|
||||
}
|
||||
}
|
||||
}
|
||||
// 1. Magic Rarity: 50% chance to get a Magic Prefix (1 stat change)
|
||||
else if (rarity == ItemRarity.magic) {
|
||||
if (random.nextBool()) { // 50% chance
|
||||
// Filter valid prefixes for this slot
|
||||
final validPrefixes = ItemPrefixTable.magicPrefixes.where((p) {
|
||||
return p.allowedSlots == null || p.allowedSlots!.contains(slot);
|
||||
}).toList();
|
||||
|
||||
if (validPrefixes.isNotEmpty) {
|
||||
final modifier = validPrefixes[random.nextInt(validPrefixes.length)];
|
||||
finalName = "${modifier.prefix} $name";
|
||||
|
||||
modifier.statChanges.forEach((stat, value) {
|
||||
switch(stat) {
|
||||
case StatType.atk: finalAtk += value; break;
|
||||
case StatType.maxHp: finalHp += value; break;
|
||||
case StatType.defense: finalArmor += value; break;
|
||||
case StatType.luck: finalLuck += value; break;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// 2. Rare Rarity: Always get a Rare Prefix (2 stat changes or stronger)
|
||||
else if (rarity == ItemRarity.rare) {
|
||||
bool nameChanged = false;
|
||||
|
||||
// Always generate a completely new cool name for Rare items
|
||||
finalName = NameGenerator.generateName(slot);
|
||||
nameChanged = true;
|
||||
|
||||
// Filter valid prefixes for this slot
|
||||
final validPrefixes = ItemPrefixTable.rarePrefixes.where((p) {
|
||||
return p.allowedSlots == null || p.allowedSlots!.contains(slot);
|
||||
}).toList();
|
||||
|
||||
if (validPrefixes.isNotEmpty) {
|
||||
final modifier = validPrefixes[random.nextInt(validPrefixes.length)];
|
||||
|
||||
// If name wasn't already changed by NameGenerator, apply prefix to name
|
||||
if (!nameChanged) {
|
||||
finalName = "${modifier.prefix} $name";
|
||||
}
|
||||
// Even if name changed, we STILL apply the stats from the prefix modifier!
|
||||
// Because NameGenerator is just visual flavor, stats come from the modifier.
|
||||
|
||||
modifier.statChanges.forEach((stat, value) {
|
||||
switch(stat) {
|
||||
case StatType.atk: finalAtk += value; break;
|
||||
case StatType.maxHp: finalHp += value; break;
|
||||
case StatType.defense: finalArmor += value; break;
|
||||
case StatType.luck: finalLuck += value; break;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
// Legendary/Unique items usually keep their original names/stats as they are special.
|
||||
|
||||
return Item(
|
||||
id: id,
|
||||
name: finalName,
|
||||
description: description,
|
||||
atkBonus: finalAtk,
|
||||
hpBonus: finalHp,
|
||||
armorBonus: finalArmor,
|
||||
slot: slot,
|
||||
effects: effects,
|
||||
price: price,
|
||||
image: image,
|
||||
luck: finalLuck,
|
||||
rarity: rarity,
|
||||
tier: tier,
|
||||
);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
import 'dart:math';
|
||||
import '../model/item.dart';
|
||||
import '../data/item_table.dart'; // For ItemTemplate
|
||||
import '../data/item_prefix_table.dart';
|
||||
import '../data/name_generator.dart';
|
||||
import '../enums.dart';
|
||||
|
||||
class LootGenerator {
|
||||
static final Random _random = Random();
|
||||
|
||||
/// Generates an Item instance from a template, applying prefixes/suffixes based on rarity.
|
||||
static Item generate(ItemTemplate template) {
|
||||
String finalName = template.name;
|
||||
int finalAtk = template.atkBonus;
|
||||
int finalHp = template.hpBonus;
|
||||
int finalArmor = template.armorBonus;
|
||||
int finalLuck = template.luck;
|
||||
|
||||
// 0. Normal Rarity: Prefix logic for base stat variations
|
||||
if (template.rarity == ItemRarity.normal) {
|
||||
// Weighted Random Selection
|
||||
final prefixes = ItemPrefixTable.normalPrefixes;
|
||||
int totalWeight = prefixes.fold(0, (sum, p) => sum + p.weight);
|
||||
int roll = _random.nextInt(totalWeight);
|
||||
|
||||
ItemModifier? selectedModifier;
|
||||
int currentSum = 0;
|
||||
for (var mod in prefixes) {
|
||||
currentSum += mod.weight;
|
||||
if (roll < currentSum) {
|
||||
selectedModifier = mod;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedModifier != null) {
|
||||
if (selectedModifier.prefix.isNotEmpty) {
|
||||
finalName = "${selectedModifier.prefix} $template.name";
|
||||
}
|
||||
|
||||
double mult = selectedModifier.multiplier;
|
||||
if (mult != 1.0) {
|
||||
finalAtk = (finalAtk * mult).floor();
|
||||
finalHp = (finalHp * mult).floor();
|
||||
finalArmor = (finalArmor * mult).floor();
|
||||
}
|
||||
}
|
||||
}
|
||||
// 1. Magic Rarity: 50% chance to get a Magic Prefix (1 stat change)
|
||||
else if (template.rarity == ItemRarity.magic) {
|
||||
if (_random.nextBool()) { // 50% chance
|
||||
// Filter valid prefixes for this slot
|
||||
final validPrefixes = ItemPrefixTable.magicPrefixes.where((p) {
|
||||
return p.allowedSlots == null || p.allowedSlots!.contains(template.slot);
|
||||
}).toList();
|
||||
|
||||
if (validPrefixes.isNotEmpty) {
|
||||
final modifier = validPrefixes[_random.nextInt(validPrefixes.length)];
|
||||
finalName = "${modifier.prefix} $template.name";
|
||||
|
||||
modifier.statChanges.forEach((stat, value) {
|
||||
switch(stat) {
|
||||
case StatType.atk: finalAtk += value; break;
|
||||
case StatType.maxHp: finalHp += value; break;
|
||||
case StatType.defense: finalArmor += value; break;
|
||||
case StatType.luck: finalLuck += value; break;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// 2. Rare Rarity: Always get a Rare Prefix (2 stat changes or stronger)
|
||||
else if (template.rarity == ItemRarity.rare) {
|
||||
bool nameChanged = false;
|
||||
|
||||
// Always generate a completely new cool name for Rare items
|
||||
finalName = NameGenerator.generateName(template.slot);
|
||||
nameChanged = true;
|
||||
|
||||
// Filter valid prefixes for this slot
|
||||
final validPrefixes = ItemPrefixTable.rarePrefixes.where((p) {
|
||||
return p.allowedSlots == null || p.allowedSlots!.contains(template.slot);
|
||||
}).toList();
|
||||
|
||||
if (validPrefixes.isNotEmpty) {
|
||||
final modifier = validPrefixes[_random.nextInt(validPrefixes.length)];
|
||||
|
||||
// If name wasn't already changed by NameGenerator (fallback?), apply prefix to name
|
||||
// But NameGenerator always returns a name.
|
||||
// However, if we want to combine "Prefix" + "GeneratedName", we could.
|
||||
// Current logic in ItemTable was:
|
||||
if (!nameChanged) {
|
||||
finalName = "${modifier.prefix} $template.name";
|
||||
}
|
||||
// Wait, the logic in ItemTable says:
|
||||
// "If name wasn't already changed by NameGenerator, apply prefix to name"
|
||||
// But just above it says "nameChanged = true;". So it never enters inside?
|
||||
// Actually, NameGenerator might fail? No, it's static data.
|
||||
// Let's stick to the logic: Rare items use generated names, but get STATS from prefix.
|
||||
|
||||
modifier.statChanges.forEach((stat, value) {
|
||||
switch(stat) {
|
||||
case StatType.atk: finalAtk += value; break;
|
||||
case StatType.maxHp: finalHp += value; break;
|
||||
case StatType.defense: finalArmor += value; break;
|
||||
case StatType.luck: finalLuck += value; break;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
// Legendary/Unique items usually keep their original names/stats.
|
||||
|
||||
return Item(
|
||||
id: template.id,
|
||||
name: finalName,
|
||||
description: template.description,
|
||||
atkBonus: finalAtk,
|
||||
hpBonus: finalHp,
|
||||
armorBonus: finalArmor,
|
||||
slot: template.slot,
|
||||
effects: template.effects,
|
||||
price: template.price,
|
||||
image: template.image,
|
||||
luck: finalLuck,
|
||||
rarity: template.rarity,
|
||||
tier: template.tier,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user