6
\$\begingroup\$

For an RPG I want to generate a world with lots of NPCs, and the NPCs all have relations to eachother. They can be eachothers parents, children, siblings, friends, bosses, lovers, lieges, vassals, etc. And they have an opinion of eachother between -100 (hate) and +100 (love). If person A is close, like the child or lover, of person B that gives a bonus (say, +50) to B's opinion of A, etc.

This system is closely inspired by how relationships between all the characters in Crusader Kings 2 work. It can also be compared to The Sims.

Ideas

I am wondering what would be the sensible way to store this kind of data? Here are the three main approaches I can think of.

1. Object oriented: In the object oriented approach I make a Character class and a Relation class. An instance of the Relation class has pointers to two different Character instances and information about the relation between them. I have to store an array of Characters and an array of Relations. Probably also each Character instance should have an array of pointers to the Relations she has (maybe the global array of Relations is not necessary then?). A Relation instance does not necessarily need to exist between two characters that does not know eachother (as opposed to Crusader Kings, where every character has an opinion of everyone), but I can add new Relations if two NPCs starts to interact.

# Pseudocode concept of what a Relation instance could contain
Relation:
    chars = [charA, charB]
    opinions = 50, 60
    relations = ["child", "parent"]

2. Normalized database: Have a Character table and a Relations table. The Relations table must have two columns, charA_id, charB_id, which references two unique Characters. This approach is quite similar to the object oriented one, just that each Character and Relation is a row in their respective tables instead of instances of a class.

3. Matrix: The Characters can be either class instaces or a database, but the Relations are stored in a big n*n matrix, where n is the number of Characters that exists. In this approach every NPC would have an opinion of every other, whether or not they actually know them. This will store an abundance of information, but in a very simple format. With a few 100s of NPC the size of matrix will be in the 10000s. This could work well if I just wanted a simple opinion number for eachother, but as I also want to store the nature of the relation (parent, lover, vassal, etc.) that can be clumsy to put in a matrix.

What it is

I'm not making a video game. This system is a tool to generate a world for tabletop RPGs like Dungeons & Dragons. The goal is to be able to quickly populate a fictive world with lots of procedurally generated characters to give the game master a rough slate from which to flesh out more details. If this project is successful it could be very useful for grand campaigns with lots of social interactions. The game master could plop down a new city full of people for the players to interact with in a matter of minutes.

Since it is not a game per se, the way of storing the information and having a good way to view it is the most important. The benefit of making a database is that I can perform queries like "Give me all the blacksmiths in city X" or "Give me all the female family members of person Y". The object oriented approach has the benefit of being more powerful in that it is very flexible in what kind of information I can put in a class, and I can make functions, like generateChild(personA, personB). I might go for a hybrid approach. Even if I make the system object oriented I could still convert it into a database to query against.

The final result will be

  1. A program that can generate (and change) chunks of new Characters and Relations and store them to file(s) in some format.
  2. An interface to search through and view the data.

Question

My question is what would be the sensible way to store this kind of data? Which of my ideas are good/bad? Do you have other suggestions?

Are there other games than Crusader Kings and The Sims that does something like this? How is it typically done? Relations between Factions can also be a thing, not just between individual Characters.

Example

I made an object oriented prototype that can generate random characters like these. Each character has their relations listed under them (not all the relations make logical sense at the moment).

name: Pupyq
age: 79
gender: female
lifestyle: wretched
alignment: neutral neutral
  niece/nephew: Tesemakebi (31)
  cousin: Tesemakebi (52)

name: Hyp
age: 37
gender: male
lifestyle: modest
alignment: neutral evil
  cousin: Nyle (-69)
  employee: Agegixuku (-86)
  parent: Qogilatyji (95)

name: Agegixuku
age: 21
gender: male
lifestyle: poor
alignment: lawful neutral
  employee: Zizewib (48)
  employer: Hyp (-41)
  employee: Hagimiqox (72)
  child: Imibimataz (-85)

name: Qogilatyji
age: 80
gender: female
lifestyle: squalid
alignment: neutral good
  aunt/uncle: Zizewib (-14)
  child: Hyp (81)

name: Tesemakebi
age: 53
gender: female
lifestyle: poor
alignment: neutral good
  aunt/uncle: Pupyq (29)
  liege: Ovi (95)
  employer: Zizewib (-43)
  cousin: Pupyq (79)

name: Ovi
age: 74
gender: male
lifestyle: squalid
alignment: neutral neutral
  vassal: Tesemakebi (100)
  aunt/uncle: Hagimiqox (-82)

name: Hagimiqox
age: 37
gender: male
lifestyle: wretched
alignment: neutral evil
  employer: Agegixuku (36)
  niece/nephew: Ovi (-41)

name: Imibimataz
age: 76
gender: male
lifestyle: comfortable
alignment: neutral neutral
  parent: Agegixuku (-100)

name: Nyle
age: 68
gender: male
lifestyle: wretched
alignment: chaotic neutral
  cousin: Hyp (-65)

name: Zizewib
age: 34
gender: female
lifestyle: wealthy
alignment: lawful neutral
  niece/nephew: Qogilatyji (-36)
  employer: Agegixuku (30)
  employee: Tesemakebi (-52)

name: Ile
age: 16
gender: male
lifestyle: wretched
alignment: chaotic good
  sibling: Pocohyjep (12)
  aunt/uncle: Qegalydu (45)
  employer: Pocohyjep (-29)

name: Qehiqiqej
age: 69
gender: male
lifestyle: poor
alignment: lawful good
  spouse: Qegalydu (87)

name: Omux
age: 72
gender: female
lifestyle: modest
alignment: neutral evil

name: Qegalydu
age: 15
gender: male
lifestyle: aristocratic
alignment: chaotic good
  spouse: Qehiqiqej (58)
  child: Pocohyjep (68)
  niece/nephew: Ile (4)

name: Pocohyjep
age: 63
gender: male
lifestyle: wealthy
alignment: neutral evil
  sibling: Ile (-8)
  parent: Qegalydu (75)
  employee: Ile (-6)
\$\endgroup\$
1
\$\begingroup\$

Mostly it comes down to how you expect people to interact with your data.

Database approach
The database approach is best suited to situations with an emphasis on querying. Will the DM often want to see "all the NPCs that are evil, living in poverty and have a 2nd degree or stronger relationship to Duke Gettenrobbed?" Databases are ideal for this, so it makes sense to use this format. There's a variety of database applications that provide UI support for adding, removing, editing, and querying the data. As such you can focus more on your core development (generating interesting data sets) and not get bogged down developing UI.

OO approach
The OO approach makes more sense if the users don't need or want the complexity of a data base application. For instance, if the data set is modest enough to handle comfortably in a text editor. That said, I would strongly encourage you to present the data in format that is both human readable and follows a standardized format such as JSON or YAML. Using a stock data interchange format instead of something homemade has some advantages:

  • there's usually libraries already available
  • it's easier for others to deal with your data
  • less time focused on data format means more time for core development

This is the route I used for entity data in a project. I didn't have a ton of entities to keep track of and I want something that I could quickly edit by hand. I like YAML since it supports comments and is a super-set of JSON, but in my opinion JSON seems to be more popular and thus have more support in terms of tools and libraries. Here's an example where I took your data & tweaked it a bit to fit the format I was using:

---
Version: 1.20.3
Date: 1-02-1997
Data:
  - Person:
      ID: 001
      Name: Pupyq
      Age: 79
      Gender: female
      Lifestyle: wretched
      Alignment: neutral neutral
      Relationships:
        - PersonID: 002
          Flow: Equal
          Type: Family
          Time: Current
          Phrase: spouse of
          Opinion : 90
        - PersonID: 003
          Flow: Lesser
          Type: Work
          Time: Past
          Phrase: purchase goods from
          opinion : dislike
  - Person:
      ID: 002
      Name: Hyp
      Age: 37
      Gender: male
      Lifestyle: modest
      Alignment: neutral evil
      Relationships:
        - PersonID: 001
          Flow: Equal
          Type: Family
          Time: Current
          Phrase: spouse of
          Feeling: love
        - PersonID: 003
          Flow: Equal
          Type: Social
          Time: Present
          Phrase: plays cards with
          Feeling: 30
...

Note: in my system, relationship are either equal or directional. For instance, the worker / employer relationship is directional and I designate it as flowing from the employer to the employee. Including this directly as a field simplified a lot of code for phrase generation. Similarly, including unique identifiers (rather than relying on names) simplified things for me. And I wanted to support previous relationships that have since ended (former employees, lovers, etc), so there's a field for that too. If these features help you too, great; if not, drop them.

One final note: while you could use XML, I find it to be too tag heavy to be easily dealt with by hand.

Matrix approach
In most respects, I find the matrix option is the weakest. Unless you go through the trouble of using a sparce matrix, most of the entries are wasted space. For the computer, this is only a problem if your data set is truly huge. However it's still a problem for the human user; reading a massive, sprawling matrix is unwieldy.

The human usability factor gets even worse if you have multi-dimensional data. You counter some of that by breaking the data across multiple, more narrowly focused matrices (social networks, employment networks, etc). That said, matrices are nice if you're dealing with printed hard copy and want to quickly cross reference things. For instance, if the data were to be printed out as a supplement for a play session.

\$\endgroup\$
  • \$\begingroup\$ Thank you for the input. I like your suggestions and they seem in line with what I have been thinking. Categorizing and hierarchicalizing the relations is definitely good. I do imagine having 100s of NPCs and then I think a lot of the power/usefulness would lie in such queries, so I'll probably go for a database storage. It could still be an option to show query results as JSON-like for better readability on specific characters. \$\endgroup\$ – PaulMag Apr 20 at 17:05
1
\$\begingroup\$

First of all im not very versed in Database structures. I know how they work, what they are there for, and since Uni never had to touch them again.

As a DnD player myself, i see some problems with the pure numbers approach. Many relations depend on the history of the two characters, family members of them or even guild members or just pure gossiping. And what does a +21 relation mean between brothers in comparison two people that barely know each other. If you know how do rate those numbers, that good, but i certainly would expand on it.

To your ideas:

  • Object oriented
    This approach should deliver the most in-depth information about relations, characters and their standings. If you only work offline, the size of those relation classes shouldn't be a problem and you could even add the option to store information about why their relationship is so bad. Maybe add an 'incidents' class to store those moments which changed their standings with important information. The OO approach is definitely the most flexible and even in a sample size of 100 people this should be around 4950 relationships at max.
  • Normalized DB
    A database is the best method to store the simple relationships, as you can basically use Person A relation to Person B as a composite key (So A,B would be the key and the relationship the value). As stated before im not quite sure if it is easy to handle changes in the system itself, adding new tables or new foreign keys.
    Problem would be, that you would have to save more complex in other ways, e.g. Pictures. Also im not quite sure how easy it is to store larger text like descriptions or longer quotes.
    You can add families, general relationship modificators shaded by a familie towards another familie etc.
  • Matrix
    The pure number maxtrix is basically a map of Person A to a map of Person B to a number. Every important information is missing. But if you map Person B to a relation class instance, than that should be the best OO for quick relation access, but worst if you want query results.

So depending on the stored informations a DB should be the best. Everything you need in addition the simple DB functions you can add with small container classes (like for pictures).

\$\endgroup\$
1
\$\begingroup\$

I'd say this is very similar to some old (non-game related) projects in which I worked. What worked there, and I'd say is a sensible solution, is to code the logic in your programming language of choice (our user interface was an Intranet page) and then store the all the information in a database. I am not sure how you are coding your tool, but a number of programming languages have libraries to offer support/integration to DB systems (we used Java, at the time).

As a note, remember that a Relationship table might include additional information about itself. To give an example, you can have a column called active (0 or 1) which would easily enable you to query "give me a list of former associates of X". Or, instead of 1 and 0, you could have an Event_ID, which would be a table storing the information of what happened. Then you could present/report/save this information through an user interface programmed in Python/Java/Binary or whatever you are most comfortable with :)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.