Where do you keep NPC data in a multi-map game?

I am trying to implement Fallout/Skyrim-style NPCs where NPCs always have a location (even if that location is not in the currently-loaded level) and have persistent properties that can change at runtime, such as inventory, relationship level, XP, etc. Has anyone successfully implemented this in a “clean” way, and why did you decide to do it that way vs. some other way?

Right now I am keeping NPC data (stats, inventory, etc.) right on the NPC character pawn. But this isn’t going to work across levels because those actor objects are going to be destroyed when the level is unloaded. It’s obviously also not going to work for storing locations, because I won’t have an instance of the pawn in the level if their current location is in some other level.

It seems the GameInstance is where those data need to be stored if they’re going to persist between levels. So each NPC’s inventory, location, etc. should be available in the GameInstance. But if I’m going to store them in the GameInstance anyway, should I even bother copying properties like inventory, xp, etc. to the NPC pawns, or should I just read from/write to the GameInstance? It seems like if have copies of the data in multiple places, it’s easy for them to get desynchronized.

Another option could be to save the data via a SaveGame object when a level is unloaded and reload it when the new level is loaded, but this seems like a roundabout way of doing it and could cause slower load times due to I/O overhead.

Can anyone who’s implemented such a thing describe how they’ve done it, and why?

I use a combination of both game instance and save game objects. I have a save object for each actor that needs to be saved and I structure the slot names so they can be saved and loaded with level changes.

When the game starts, the game instance loads in a ‘Master Save’ save game object that has an array of all of the MasterGameSaveNames. This lets the player pick which save name to load which is then set in the Game Instance as the MasterGameSaveName so that its accessible on any level at any time.

Each actor’s saved slot is templated as: MasterGameSaveName.LevelName.DisplayName

When the player initiates a level change, an interface call goes to all the actors with the Save Game and the level unloads once all the actors have finished saving their current state (position, inventory, and current objective).

When a new level starts, each actor has an initialize function the checks to see if the templated save slot exists. If not, then it loads the default sate, if it does it loads the state from the save object.

The nice things about this system is that it lets the player have multiple story lines and saves. Its also pretty flexible because I don’t need a data table, each actor is responsible for itself and I don’t need to update any existing code when I add new actors or change things.

Hope this helps!

Thanks for the answer - very helpful