Case File 02 Open World · NDA Systems / Data · 8 MIN READ

Modular Inventory
& Progression:
Six Weeks to Unblocked

Four months in prototype, no working save pipeline, no agreed data model. This file documents the foundation that ended the stall — data assets over hard-coded item classes, a save format decoupled from actor state, and progression wired to GAS attribute sets rather than a parallel stat system.

RS
Reignance Studios // UE5 SYSTEMS & ARCHITECTURE · NDA-PROTECTED ENGAGEMENT

Outcome

A four-month prototype stall ended in six weeks with a modular inventory, equipment, loot and progression foundation the team shipped their MVP on — and continued extending without further contract work.

4MONTHS STALLEDPrototype state before engagement
6WEEKSFoundation delivered
MVPON SCHEDULEShipped by the team after handoff
0PARALLEL STAT SYSTEMSProgression reads GAS attributes
// NDA Notice

Client name, project title and identifying detail are withheld under mutual NDA. Figures are as reported at handoff. Everything documented below is technique, which is the part that transfers.

The Stall

The project was an open-world UE5 title that had been in prototype for four months. The team was not blocked on skill or effort. They were blocked on a decision nobody had made: what an item is.

Three partial inventory implementations existed in the codebase. One used Blueprint structs, one used a hard-coded item class hierarchy, one was mid-migration between the two. Each worked for the feature it was written for. None could load the others’ data, and no save system could be written until one of them won.

// Engagement Context

Six-week fixed-scope engagement on an existing UE5 open-world project. The deliverable was a foundation the team owns, not a finished feature set. Client and project withheld under mutual NDA.

The Actual Blocker

The reported problem was that the save system did not work. The save system did not work because it could not be written. A save pipeline serialises a data model, and there were three.

This is the most common shape of a prototype stall on an open-world project, and it is worth naming precisely: the team was iterating on features that all depended on an unmade foundational decision, so every week of work made the eventual migration larger rather than smaller.

A prototype that will not converge is usually not short of features. It is short of one decision that three features are each quietly assuming a different answer to.

The first deliverable was therefore not code. It was a one-page written decision on the item model, reviewed with the team, that everything after would be built against.

The Data Model

Items became data assets, not classes. A UPrimaryDataAsset definition per item describes what the item is; a lightweight runtime instance struct describes what this particular copy of it currently is.

  • Definition — data asset, immutable. Identity, display data, stack rules, equipment slot, the Gameplay Effects granted while equipped, and the Gameplay Tags it carries.
  • Instance — struct, mutable, replicated. A reference to its definition, stack count, durability or charges, and any rolled modifiers. Small, cheap to replicate, trivial to serialise.
  • Container — component. An array of instances plus the slot rules that govern it. Inventory, equipment, loot container and vendor stock are all the same component with different rules.

The consequence designers cared about: a new item is a data asset created in the editor, not a C++ class and a recompile. The consequence engineering cared about: the replicated payload for a full inventory is a small array of structs, not a set of actor references with lifetime problems attached.

Loot tables became data assets over the same definitions, weighted, with rolled modifiers applied at generation time and stored on the instance — so a dropped item is fully described by its instance and never needs the generator re-run to be understood.

The Save Pipeline

With one model, the save pipeline is small. It serialises instance structs and container contents, never actor state, and it was versioned from the first commit.

  • A save version integer in the header of every save, with a migration function per version step — added before it was needed, because retrofitting versioning onto shipped saves is not possible without breaking players.
  • Definitions referenced by a stable identifier rather than by asset path, so moving or renaming an asset does not invalidate existing saves.
  • Save and load exercised in automated tests over a generated inventory containing every item type, run in CI rather than by hand.
// Version the Save Format on Day One

It costs an integer and a switch statement before launch. After launch, without it, every data model change is a choice between breaking saves and never changing the model again.

Progression on GAS Attributes

The project already used GAS for combat. Progression was therefore built on attribute sets rather than beside them — a deliberate choice to avoid the second most common source of long-term drift in an RPG codebase.

Equipment grants an infinite-duration Gameplay Effect while equipped and removes it on unequip. Levelling applies a Gameplay Effect to base attributes. Nothing anywhere adds a stat by direct assignment.

The result is a single query path: an actor’s current strength is the attribute value, and every modifier contributing to it is inspectable through the ASC at runtime. When a designer asks why a number is what it is, the answer is enumerable rather than archaeological.

  • Equipment stats — an infinite GE granted on equip and removed on unequip. No manual add and subtract, so nothing leaks when an unequip path is missed.
  • Level-ups — a GE applied to base attributes, replayable from the save rather than stored as a derived total.
  • Consumables — instant or duration GEs, already the correct shape, no separate system required.

What Transfers

Independent of this project:

  • If a prototype will not converge, look for the unmade foundational decision before adding engineers to the feature list.
  • Items are data, not classes. Definition assets plus lightweight runtime instances keeps designers unblocked and the replicated payload small.
  • Version the save format in the first commit that writes a save.
  • Reference definitions by stable identifier, never by asset path.
  • If GAS is already in the project, progression belongs on attribute sets. A parallel stat system is a divergence with a delayed fuse.