ItemDrops 2.0: ItemVault & Inventory Integration
ItemDrops 2.0: Inventory & ItemVault Integration
Goals
- Stay inventory-agnostic: ItemDrops Core only knows about drops and contexts.
- First-class ItemVault support: Provide a thin, testable bridge so games using ItemVault can plug in easily.
- Encourage cross-plugin compatibility: The same patterns work for other inventory systems.
This guide assumes you are using ItemDrops 2.0 (C#) and, optionally, ItemVault.Core for inventories.
This page is the canonical integration guide for ItemDrops 2.0 and ItemVault. Internal docs and tests should point back here as the primary source.
1. Core Concepts
DropService + LootContext
At the core of ItemDrops 2.0 is DropService, which wraps a DropTableRegistry and the DropCalculator:
var registry = new DropTableRegistry();
var dropService = new DropService(registry);
// Register a table
registry.RegisterDropTable("default_enemy", enemyTable);
// Build a loot context from game state
var context = new LootContext
{
Level = 10,
LuckModifier = 1.25f, // game-derived luck scalar
EntityType = "enemy"
};
// Generate drops
var results = dropService.GenerateDrops("default_enemy", context);
Key points:
LootContextis immutable and used by public APIs.DropContextis a mutable core context used internally and by some legacy/engine APIs.DropCalculatorbridges between them:- Overloads accept both
LootContextandDropContext. - Internally, conditions and tables run on
DropContext; metadata usesLootContext.
- Overloads accept both
LuckModifier behavior
LootContext.LuckModifier (and DropContext.LuckModifier) scales table drop chance:
// Effective chance = table.DropChance * LuckModifier, clamped [0,1]
var effectiveChance = dropTable.DropChance * context.LuckModifier;
LuckModifier = 0→ table never drops.LuckModifier = 1→ table uses its configuredDropChance.LuckModifier > 1(with low base chance) → chance is boosted up to 100% (capped).
This gives you a single scalar hook the game can compute from stats, world events, difficulty, etc.
Global conditions & modifiers (legacy)
DropTableCollection.GlobalConditionsandGlobalModifiersstill exist as config fields.- DropCalculator 2.0 does not apply them at runtime.
- All behavior should be expressed via:
- Per-drop conditions/modifiers.
- The calling code (by choosing tables, building context, or using a separate effects pipeline).
This is the “Option B” simplification: no hidden global effects in the core calculator.
2. Inventory-Agnostic Integration via IInventoryIntegration
ItemDrops 2.0 continues to use the IInventoryIntegration abstraction to talk to any inventory system:
public interface IInventoryIntegration
{
string Id { get; }
string Name { get; }
string Description { get; }
int Capacity { get; }
int CurrentCount { get; }
bool IsFull { get; }
bool IsEmpty { get; }
bool CanAcceptDrops(IEnumerable<DropInstance> drops);
InventoryAddResult AddDrops(IEnumerable<DropInstance> drops);
InventoryAddResult AddDrop(DropInstance drop);
int GetItemQuantity(string itemId);
bool HasItem(string itemId);
IEnumerable<DropInstance> GetAllItems();
bool IsValid();
}
Typical flow:
DropServicegeneratesDropResult/DropInstancecollections.- Game code (or a small adapter) converts those into final
DropInstances for inventory. IInventoryIntegrationadds them to your inventory system.
This keeps ItemDrops Core free of inventory concerns.
3. ItemVault Option A Bridge (Recommended Pattern)
For games that use ItemVault.Core, we recommend a thin bridge in an infra project that references both cores.
Conceptually:
internal sealed class ItemVaultInventoryIntegration : IInventoryIntegration
{
private readonly IInventoryService _inventoryService;
private readonly OwnerId _ownerId;
public ItemVaultInventoryIntegration(IInventoryService inventoryService, OwnerId ownerId) { ... }
public InventoryAddResult AddDrops(IEnumerable<DropInstance> drops)
{
var result = new InventoryAddResult();
foreach (var drop in drops)
{
var granted = _inventoryService.TryGiveItem(_ownerId, drop.ItemId, drop.Quantity);
if (granted)
{
result.ItemsAdded += drop.Quantity;
result.AddedItems.Add(drop);
}
else
{
result.ItemsRejected += drop.Quantity;
result.RejectedItems.Add(drop);
}
}
result.Success = result.ItemsRejected == 0;
return result;
}
// CanAcceptDrops, GetItemQuantity, HasItem, GetAllItems, etc.
}
Notes:
- Lives in a separate infra layer (e.g.
ItemDropsItemVault.Infra) so neither plugin depends on the other. - Uses only:
ItemDrops.Coretypes (DropInstance,IInventoryIntegration).ItemVault.Coretypes (IInventoryService,OwnerId,Inventory).
- Fully testable in isolation (see
ItemDropsItemVault.Infra.Tests).
Example test scenario
var inventoryService = new InventoryService();
var ownerId = new OwnerId("owner-1");
var bridge = new ItemVaultInventoryIntegration(inventoryService, ownerId);
var drop = new DropInstance(
itemId: "test_potion",
itemName: "Test Potion",
itemType: "Item",
quantity: 3,
rarity: ItemRarity.Common,
weight: 1.0f);
var result = bridge.AddDrop(drop);
result.Success.ShouldBeTrue();
bridge.GetItemQuantity("test_potion").ShouldBe(3);
This demonstrates the Option A mapping from ItemDrops-style drops into an ItemVault inventory.
4. Thistletide & Per-Session Services (Overview)
In Thistletide, game systems are wired around a per-session spine (GameUserSessions, WorldTime, ItemDrops, ItemVault, etc.). A common pattern is:
- A fixture or factory that knows the current
GameSessionId/UserId. - Helper methods like
CreateTimeServiceForCurrentSession()orCreateDropServiceForCurrentSession().
Going forward, you can generalize this into a small service factory interface, for example:
public interface IGameSessionServiceFactory
{
TService GetServiceForCurrentSession<TService>() where TService : class;
}
- Implementation details (dictionary keyed by session + type, composition root wiring, etc.) are left to the game.
- ItemDrops and ItemVault stay pure core; they do not depend on the factory.
- Thistletide (or other games) can adopt this pattern to keep wiring consistent across services.
5. Versioning Notes (2.0)
- The ItemDrops C# core used in this guide is considered 2.0 for documentation and integration purposes.
- GDScript plugin version numbers may differ (e.g.,
plugin.cfgversion="0.4.x") and are treated as a separate track during the porting phase. - Data-driven drop table configuration remains schema version
"1.0"unless otherwise noted; future schema versions will be documented separately.
For an overview of the architecture and porting status, see:
docs/item-drops/content/item_drops/architecture.mddocs/PORTING_GDSCRIPT_TO_CSHARP_WORLD_TIME_AND_ITEM_DROPS.md
Last Updated: December 2025
ItemDrops C# Core: 2.0 (documentation target)