Yes... looks exactly like a design problem.
However, it's not as easy to make CreateFromNPC as it heavily uses NPC's variables and relates to TNPC structure (very tricky, includes "conclusions" which aren't available as direct variables, I believe it'll also use its private fields in future), returning (very simple) TContext.
So... NPC "thinks" and returns some "context" (e.g. list of gender, nationality, disposition towards listener and relation towards subject spoken of, time of the day, place, etc) which governs which dialogue options it'll present to the "world". It'll eventually become dynamic and will return TContext only relating to a specific "request" by TDialogueContext to optimize memory and CPU consumption.
On the other hand TDialogueContext is a "roof-top" or "extension" of the TContext which not only stores the dialogue context, but also governs its creation run-time. The total list of TContext will be made based on many world parameters including NPCs speaking to each other.
So:
It's NPC responsibility to provide its context as a list of TContext (according to an external request: e.g. "What do you think of %npc2?" will provide %npc1 disposition towards %npc2, what he (dis)likes about him, how well they are acquainted, some outstanding facts about %npc2 in memory, etc).
TDialogueContext is the one "requesting" NPCs to provide context for the current dialogue topic.
Example:
- Hi, Andry! Haven't seen you for ages! (context: %npc1 and %npc2 are friends, %npc1 didn't meet %npc2 for some time, %npc2 request name, generic: greeting (suggest next slot: greeting-response)
- Hi, Jay! Nice to meet you! (context: %npc2 and %npc1 are friends, accept suggested slot: greeting-response (don't break the dialogue by some "I hate you. Get lost from my sight!" with suggest next slot: end-dialogue), %npc2 is "glad" (or pretending to be glad), suggest next slot: random-chat).
- You know, I've saw Reann yesterday. (select random-chat dialogue slot: gossip. Use memory (%fact1) about %npc3. Suggest next dialogue slot (self): continue) She was wearing a really nice red shoes. (listener didn't interrupt the dialogue (was ok with next dialogue slot), disposition to %npc3 check, use memory (%fact2 related to %fact1), check values of %npc1 (he likes red color), select next slot: react-gossip)
- Red? I never heard her of liking red color! (accept react-gossip slot, check %fact2 with %npc2.memory(about %npc3) and it contradicts result = context:"surprise", suggest next slot react-surprise).
- I tell ya. (accept react-surprise slot, suggest next slot: random-chat or goodbye)
UPD: The overall idea is the following.
There is a huge pool of TPhrase with DEMAND and ALLOW TContext (i.e. context where the phrase might be used).
There is a TDialogueContext with DEMAND and ALLOW TContext based on current game situation (speaker, listener, topic, world parameters).
TDialogueContext scans through the pool of phrases and checks if:
all items in TDialogueContext.DEMAND must be found in TPhrase.DEMAND and TPhrase.ALLOW
all items in TPhrase.DEMAND must be found in TDialogueContext.DEMAND and TDialogueContext.ALLOW
A list of TPhrases that meet these two requirements is generated and a random TPhrase is chosen from the list.
Example:
TPhrase.text := 'Hi, (request listener:name, nominative)!'; //if speaker and listener are friends "request listener:name" script might return nickname instead
TPhrase.context.DEMAND.add(dialogueslot_generic_greeting); //this will do for greeting_hello and greeting_response dialogue slots
TPhrase.context.DEMAND.add(disposition_above_average); //the speaker must return disposition>0.5 towards listener
TPhrase.context.DEMAND.add(informal_chat,importance=0.9); //This phrase will have to suffer 90% penalty to be used in formal situation, but still possible