Skip to main content

Item Generation

The ItemGeneratorGrain is a stateless worker that creates items with random properties.

Generation Process

Basic Generation

// Generate a rare iron sword at item level 50
var item = await generator.GenerateAsync("iron-sword", ItemRarity.Rare, 50);

Parameters

ParameterDescription
baseTypeIdWhich base type to use
rarityNormal, Magic, Rare, or Unique
itemLevelDetermines available modifier tiers

Modifier Rolling

Magic Items (1-2 mods)

private async Task<List<Modifier>> RollMagicModsAsync(BaseType baseType, int itemLevel)
{
var mods = new List<Modifier>();
var count = Random.Shared.Next(1, 3); // 1 or 2

var prefixes = await GetEligibleModifiers(ModifierType.Prefix, baseType, itemLevel);
var suffixes = await GetEligibleModifiers(ModifierType.Suffix, baseType, itemLevel);

// Roll using weighted selection
if (Random.Shared.NextDouble() < 0.5 && prefixes.Any())
mods.Add(SelectWeighted(prefixes));

if (mods.Count < count && suffixes.Any())
mods.Add(SelectWeighted(suffixes));

return mods;
}

Rare Items (3-6 mods)

  • Minimum 1 prefix and 1 suffix
  • Maximum 3 prefixes and 3 suffixes
  • Total 3-6 modifiers

Modifier Selection

Modifiers are selected using weighted random:

private Modifier SelectWeighted(List<Modifier> pool)
{
var totalWeight = pool.Sum(m => m.Weight);
var roll = Random.Shared.Next(totalWeight);

var cumulative = 0;
foreach (var mod in pool)
{
cumulative += mod.Weight;
if (roll < cumulative)
return mod;
}
return pool.Last();
}

Currency Orbs

Titan implements Path of Exile-style currency:

Transmutation Orb

Convert Normal → Magic:

public async Task<Item> TransmuteAsync(Item item)
{
if (item.Rarity != ItemRarity.Normal)
throw new InvalidOperationException("Can only transmute normal items");

var newItem = item with { Rarity = ItemRarity.Magic };
var mods = await RollMagicModsAsync(item.BaseType, item.ItemLevel);
return ApplyModifiers(newItem, mods);
}

Alteration Orb

Reroll Magic item modifiers:

public async Task<Item> AlterAsync(Item item)
{
if (item.Rarity != ItemRarity.Magic)
throw new InvalidOperationException("Can only alter magic items");

var mods = await RollMagicModsAsync(item.BaseType, item.ItemLevel);
return item with { Prefixes = mods.OfType<Prefix>(), Suffixes = mods.OfType<Suffix>() };
}

Regal Orb

Convert Magic → Rare (add 1 modifier):

public async Task<Item> RegalAsync(Item item)
{
if (item.Rarity != ItemRarity.Magic)
throw new InvalidOperationException("Can only regal magic items");

var pool = await GetEligibleModifiers(item);
var newMod = SelectWeighted(pool);

return item with
{
Rarity = ItemRarity.Rare,
Prefixes = item.Prefixes.Append(newMod).Where(m => m.Type == Prefix),
Suffixes = item.Suffixes.Append(newMod).Where(m => m.Type == Suffix)
};
}

Chaos Orb

Reroll all modifiers on a Rare item:

public async Task<Item> ChaosAsync(Item item)
{
if (item.Rarity != ItemRarity.Rare)
throw new InvalidOperationException("Can only chaos rare items");

var mods = await RollRareModsAsync(item.BaseType, item.ItemLevel);
return ApplyModifiers(item, mods);
}

Exalted Orb

Add one modifier to a Rare item:

public async Task<Item> ExaltAsync(Item item)
{
if (item.Rarity != ItemRarity.Rare)
throw new InvalidOperationException("Can only exalt rare items");

if (item.Prefixes.Count >= 3 && item.Suffixes.Count >= 3)
throw new InvalidOperationException("Item is full (6 mods)");

var pool = await GetEligibleModifiers(item);
var newMod = SelectWeighted(pool);
return AddModifier(item, newMod);
}

Socket Rolling

Jeweller's Orb

Reroll socket count:

public async Task<Item> JewellerAsync(Item item)
{
var maxSockets = GetMaxSockets(item.BaseType);
var newCount = RollSocketCount(maxSockets);
return item with { Sockets = GenerateSockets(newCount) };
}

Fusing Orb

Reroll socket links:

public async Task<Item> FusingAsync(Item item)
{
var sockets = item.Sockets.ToList();
for (int i = 0; i < sockets.Count - 1; i++)
{
sockets[i] = sockets[i] with { Linked = Random.Shared.NextDouble() < 0.3 };
}
return item with { Sockets = sockets };
}

Chromatic Orb

Reroll socket colors:

public async Task<Item> ChromaticAsync(Item item)
{
var requirements = item.BaseType.Requirements;
var sockets = item.Sockets.Select(s => s with
{
Color = RollSocketColor(requirements)
});
return item with { Sockets = sockets.ToList() };
}

Socket colors are weighted by attribute requirements:

  • High Strength → More Red
  • High Dexterity → More Green
  • High Intelligence → More Blue

Unique Items

Uniques have fixed modifiers defined in the registry:

public async Task<Item> GenerateUniqueAsync(string uniqueId)
{
var unique = await _registry.GetUniqueAsync(uniqueId);

return new Item
{
BaseTypeId = unique.BaseTypeId,
Name = unique.Name,
Rarity = ItemRarity.Unique,
Prefixes = unique.FixedPrefixes,
Suffixes = unique.FixedSuffixes,
Implicits = unique.Implicits
};
}