Null Bolt
Studio
guides

Generating items

One static call, one request object, one plain-data result. Seeds make it deterministic; a callback chain makes it yours.

The request

ItemGenerationRequest
C#
var request = new ItemGenerationRequest
{
    Template = doombringerTemplate,   // optional — pins name/icon/forced affixes
    ItemType = weaponType,            // required when no template
    SubType  = swordSubType,          // optional refinement
    Rarity   = legendaryRarity,       // required, always
    Seed     = 1337,                  // same seed → same item
    Settings = generatorSettings,     // supplies the affix pool + name config
};

ItemInstance one   = ItemGenerator.Generate(request);
ItemInstance[] lot = ItemGenerator.GenerateBatch(request, 12);   // a chest

When a template is set, its type and sub-type win over whatever the request carries. Batch generation derives a distinct deterministic seed per index from the base seed — twelve different items, all reproducible from one number.

// heads up
Invalid input returns null with a console warning — no exception. Null-check every result. The two hard requirements: a resolvable item type (directly or via template) and a rarity.

What comes back

ItemInstance propertyTypeDescription
ItemName / Description / RarityTypeLabelstringComposed name, template description, and the “Legendary Sword”-style label.
ItemType / ItemSubType / RarityrefsThe definitions that produced it.
Icon / PrefabSprite / GameObjectResolved template → sub-type → type.
BaseStats / ImplicitModifiersStatModifier[]Rolled core stats and always-on implicit rolls.
RandomAffixesGeneratedAffix[]Forced template affixes first, then the random picks — each with its rolled stat values.
SocketCountintRolled from the rarity’s range. A count — socket contents are your game’s domain.
LegendaryEffectLegendaryEffectPresent when the template supplies one and the rarity allows it; look up its EffectId in your gameplay code.
SellValue / MaxDurability / MaxStackSize / ItemLevelintEconomy numbers (formulas below). ItemLevel is metadata only.
GenerationSeed / GeneratorVersion / GenerationTimestamp / SourceTemplateNameauditEverything needed to reproduce, version-check, or trace the roll.

It's a plain [Serializable] class — no MonoBehaviour, no scene dependency. For storage, though, use snapshots rather than serializing it directly (see Saving generated items).

Seeds & determinism

The RNG is Unity.Mathematics.Random — deterministic across platforms. The contract: same seed, same definition assets, same generator version ⇒ bit-identical item. That enables reproducible chests (world seed + chest position → contents), player-reported bugs you can replay from one int, and the entire persistence model.

Reproducing a reported item
C#
// The player's screenshot shows seed 8842217 on an Epic Mystic_Staff.
var repro = ItemGenerator.Generate(new ItemGenerationRequest
{
    Template = mysticStaff,
    Rarity   = epic,
    Seed     = 8842217,
    Settings = settings,
});
// → the exact same rolls, affixes, sockets, and name.
// note
The generator stamps every item with GeneratorVersion (currently 2). When generation logic changes between asset versions, old seeds can roll differently — the version stamp is how saved snapshots detect and warn about that instead of silently drifting.

The post-generate hook

Every generated item flows through ItemGeneratorCallbacks.OnPostGenerate before it's returned — subscribers receive the instance and return it (possibly modified), chained in subscription order. It's the sanctioned place for seasonal bonuses, difficulty scaling, or analytics taps:

SeasonalBonus.cs
C#
using ItemGeneratorSystem;

public static class SeasonalBonus
{
    public static void Enable()  => ItemGeneratorCallbacks.OnPostGenerate += Apply;
    public static void Disable() => ItemGeneratorCallbacks.OnPostGenerate -= Apply;

    static ItemInstance Apply(ItemInstance item)
    {
        item.SellValue = (int)(item.SellValue * 1.25f);   // harvest-festival prices
        return item;
    }
}
// heads up
The event is static — it survives scene loads and domain reloads in the editor. Always unsubscribe (and keep handlers deterministic if you rely on seed reproduction, because they re-run on snapshot restore).

Value & durability formulas

The two formulas
text
SellValue     = floor(baseValue × rarity.SellValueMultiplier × (1 + 0.1 × affixCount))
MaxDurability = floor(baseDurability × rarity.DurabilityMultiplier)

baseValue / baseDurability come from the sub-type or type (defaults 10 / 100),
optionally overridden per template. Every affix adds +10% to sell value.