Looking for feedback on NPC placement workflow

I’m working on the level design workflow for an RPG, and currently my most labor-intensive task by far is placing filler/background NPCs around levels, the unimportant guys who walk around and say random filler stuff like “Nice weather we’re having, eh? I sure hope bigfoot doesn’t appear next time you advance the main plot wink wink”.

I’m trying to keep my entire workflow data-driven, so right now I have a unique spreadsheet for every possible gamestate, in which I add a column for every background NPC who’s supposed to spawn, and specify the NPC’s ID, the spawn point they’re supposed to appear in, and the dialogue they play when interacted with.

This works, but it’s incredibly tedious- the game has several hundred background/junk NPCs, and nearly all of them need to move in response to every major change in game state. At the very least, this means hundreds of spreadsheets to cover the entire game, each containing hundreds of individual entries.

I’m having trouble seeing how this could be improved, since a human hand is going to have to manually specify this information somewhere, but I’m wondering if there are any obvious refinements I could be making to my workflow?

Well… this is a data structure.

You could use something like a relational, or even a multidimensional, data structure for this.

So relationally you’d have (think like sql, where a table is a single spreadsheet):

A table of all NPCs
A table of all gamestates
A table of all spawn points/dialogues

The first 2 tables are as long as there are NPCs and gamestates.

Then in the spawn point table you’d have 4 columns:
npc id
gamestate id
spawnpoint
dialogue

Now you just go through and row by row you work with each npc/gamestate. I personally would go npc by npc, so something like this (note I’m using english names for ids, but you can use ints to speed it up):

NPC ID | GSTATE | POINT | DIALOGUE
Eric   |1       |...    |Hello, I am Eric.
Eric   |4       |...    |Thank you sooooo much!
Sally  |1       |...    |How's it going?
Sally  |2       |...    |Oh no, the evil emperor kidnapped Eric!
Sally  |3       |...    |Stop dilly-dallying, go save Eric!
Sally  |4       |...    |Thanks again for saving Eric.
Joe    |1       |...    |Ooga booga
Joe    |2       |...    |Spare a nickel for some beer?
Joe    |3       |...    |Evil emperor? I care not for the evil emperor. Got some beer?
Joe    |4       |...    |You bastard! You destroyed all the beer kegs in your stupid fight with the evil emperor!

Note how Eric doesn’t exist in game states 2 and 3 because he was kidnapped by the evil emperor. So he just doesn’t have entries for that.

If you need to know who Eric is, you look up Eric in the NPCs table. So if you want to know which model to use, or which house is his, or what other data you store on the NPCs table, you look it up by matching (hence the ‘relational’ bit in the name of this structure). Heck you can even store a ‘default spawn point’ on the NPC table, and that way you just put something like ‘default’ in the point column saying to just spawn him in his preferred location… saving you from having to type the same coordinates over and over.

Heck, you could even create a ‘spawn point’ table of actual positions, and the point column of the point/dialogue table you just reference the spawn point again. Foregoing the ‘default’ position, and instead you create a spawn point called ‘erics default’, or ‘by the well’, or ‘at front gate’. And then you just swap out who goes where by putting in the id of the location in the point column.

Note…

you can rearrange the order in that table I drew out.

For instance sort it by gamestate. Such a work flow might make more sense in a timeline sort of way.

NPC ID | GSTATE | POINT | DIALOGUE
Eric   |1       |ericdef|Hello, I am Eric.
Sally  |1       |salldef|How's it going?
Joe    |1       |joedef |Ooga booga

Sally  |2       |well   |Oh no, the evil emperor kidnapped Eric!
Joe    |2       |alley  |Spare a nickel for some beer?

Sally  |3       |well   |Stop dilly-dallying, go save Eric!
Joe    |3       |alley  |Evil emperor? I care not for the evil emperor. Got some beer?

Eric   |4       |gate   |Thank you sooooo much!
Sally  |4       |salldef|Thanks again for saving Eric.
Joe    |4       |well   |You bastard! You destroyed all the beer kegs in your stupid fight with the evil emperor!

I don’t know, just tossing around some ideas.

Hmm that is considerably better-organized than what I had in mind; thank you for the suggestion! :slight_smile: