ASK [NPC] about [topic]

I’m not sure how much technical detail you want, but the high-level view of how I’m approaching it is probably easiest to explain in a couple of stages. First, the “naive” dialog logic:

  • Each NPC has a data store recording their current “feelings” on different subjects. It’s basically a big hash table where the keys are subject IDs and the values are integers, where 1000 represents a neutral reaction, 0 is the worst possible reaction, and 2000 is the best possible reaction.
  • Decisions work by constructing an ordered tuple of the alternatives and using the values as weights to pick a result. E.g., summing the values, and then picking a integer between 1 and the sum. There are other methods (tending to prefer the “worst” result or the “best” result or using a linear combination of values as the weight, and so on) but the simple random-1-to-sum is the easiest to understand.
  • In conversation, the NPC is given a “board position” (usually) dictated by whatever dialog option the player most recently chose. That is a vertex in the dialog graph, and it has edges connecting to different “NPC” dialog nodes, each of which has one or more IDs associated with it, where the IDs are subjects in their decision table. The dialog decision is resolved as described above, where the decision tuple consists of all of the edge IDs for the current vertex in the dialog graph.
  • Having selected an edge, the current vertex is updated, and the edges of that vertex are the topics (or whatever you want to call them) available (and/or available by default) to the player, the the process repeats.

This is basically goal-free, in the sense that all the NPC “cares about” is their favorite option at the current moment.

In addition to this, they may have one or more goals as defined as part of their state. I’m doing this in TADS3 and the state in question is an AgendaItem (or rather a subclass of AgendaItem), but all you need to know about that is that every AgendaItem instance has a numeric priority and a isReady() method, and the game automagically makes active the (one) AgendaItem on the NPC with the highest priority says it’s ready that turn.

Agendas have been extended so that they can be associated with instances of a “goal” class. The goal class defines, in game terms, specific objectives (like obtaining some object or learning some information) and different behaviors for each stage of the “quest”. So if the objective is to obtain a pebble, then the initial stage might be “look for the pebble”, directing the NPC to engage in, for example, a random walk. If the NPC learns where the pebble is (by seeing it or by having a knowledge flag twiddled by a conversation or whatever) it might then go into a “fetch” stage, where they attempt to move to the location where the pebble is. Once in the same location as the pebble, they then move into a “take” stage, attempting to take the pebble directly if it’s on the ground or talking to whoever has it if it’s in someone’s inventory.

At each stage of the process described above, the NPCs reactions on certain subjects can be tweaked to either replace or modify their “basic” reaction on one or more subjects in their decision logic (so making them interested in the “pebble” subject even if they wouldn’t normally be, but only for the duration of the “quest”). The goal can also specify “external” goals as well, which will affect dialog choices not by directly changing the weights for the edges on the current dialog vertex, but instead directing the NPC to try to “path to” a specific node (e.g. to make the conversation about pebbles even if pebbles aren’t part of any of the adjacent dialog nodes).

To improve the performance of the pathing stuff I precompute everything and then update subgraphs as edges are added or removed. The process is the same as the one I discuss for room pathfinding in this thread.

That’s basically it. There are a bunch of special cases and tweaks (like special topics that get added to the available dialog options independent of the “local conversational context” or whatever you want to call it) but it’s mostly some elaborate (but conceptually simple) random weighted choice decision logic coupled to mostly-separate mostly-deterministic large-scale goal-seeking decision logic that either modifies or overrides the probabilistic stuff.

That was a lot of words, but I think this is one of those things that sounds a lot more complicated than it really is. Feel free to ask for clarification if I haven’t explained things well enough.