As your requirements for your dialogue system are vast and complex I am afraid there is no clear and easy solution for your problem.
The more complex the requirement the more complex the solution, but I can try to give you a few tips and directions for how I might approach a similar system.
Note that I am also unfamiliar with Dialogue Manager so I will ignore that part, my answer is however pretty generic and can probably coexist with it.
First, try to keep your code as clear as you possibly can when doing this. In complex systems it is easy to mix stuff up and have code for the same system be in a bunch of different places. I would suggest you make a function that takes 3 inputs, lets call them WorldState
, CharacterState
and DialogueOptions
. WorldState
being all the information about the current state of the world and CharacterState
being all the information about the character invoking the dialogue. Then DialogueOptions
are all possible dialogues along with their metadata for the invoking character. This way you can always just call this function whenever you need and pass it the relevant information, then get a clean result back. Lets call this function selectDialogue
.
How you manage WorldState
, CharacterState
and DialogueOptions
and how you pass them to this function I will leave up to you. I am just trying to paint a high level picture of a possible implementation.
Then it is clear to me that you need some sort of priority system. For example you might have 2 dialogue options, one normal one and one you want when a character is married to you. The married one should have higher priority and when its conditions are met should overrule the normal option. How you store this priority is again up to you, it could be a number stored next to the dialogue or it could be the order of dialogue options in an Array (probably better to avoid unnecessary calculations).
There are some advantages to determining the priority at runtime (with numbers or some other metadata). If you do this just try to only calculate the order once and not every time dialogue is invoked.
When called selectDialogue
can go through all the options from highest to lowest in priority, checking conditions and choose the first dialogue of which conditions are met. Of course always have an option that requires no conditions as a fallback if the list is exhausted. Here I suggest you store commonly used checks (like weather, marriage status, time of day, …) as properties that are attached to a dialogue and selectDialogue
knows how to check for itself.
If you require more specific logic that is maybe used for only one specific dialogue option in one character this becomes a bit more trickier as you would probably require attaching custom logic to that specific dialogue in order to decide if it is relevant. Godot does not support attaching Callable
s in the editor so this special options would have to all be created within code. While it is not impossible it is a bit tedious.
Same goes for some of the other requirements you might have. For example you may want to have an option of having multiple dialogues having the same priority and then choose between them at random or with some other arbitrary logic. For this you would have to modify the above solution and create a data structure with a bit more complexity, allowing a “dialogue” to store multiple actual dialogues and a way to then select one of them.
Now you can create a separate DialogueOptions
for every character and when dialogue with that character is invoked you call selectDialogue
passing DialogueOptions
along and get the dialogue you have to show. It allows you to clearly store each dialogue tree next to the character owning it and do not multiply dialogue selection logic across your project.
I created a very basic example of this (without custom callables and multiple dialogues at the same priority level) as I know examples are the best when trying to grasp an idea (and the salad I wrote above is probably a bit hard to digest).
Fair warning, this code is not very good and is here just to illustrate an idea. I suggest you implement this yourself in a better way.
character.gd
class_name Character
extends Node
# You can safely export this and Godot editor will
# give you a nice way of editing it.
@export var options: Array[Dialogue]
# Manage this however you like
var character_state: CharacterState
func start_dialogue():
# Here it is on you to acquire a way
# to get a reference to world handler
var dialogue = (get_node("WorldHandler") as WorldHandler).select_dialogue(character_state, options)
print(dialogue.dialogue)
class CharacterState:
var is_married: bool
var affection_level: int
dialogue.gd
class_name Dialogue
extends Resource
# All of these should be edited inside the Godot inspector
@export_multiline var dialogue = "EMPTY"
@export var marriage_required: bool
@export var time_of_day: WorldHandler.TimeOfDay
@export var affection_threshold = 0
world_handler.gd
class_name WorldHandler
extends Node
var world_state: WorldState
enum TimeOfDay {
Any,
Morning,
Day,
Evening,
}
func select_dialogue(character_state: Character.CharacterState, dialogue_options: Array[Dialogue]) -> Dialogue:
# Here we go through every dialogue, in my implementation lower array index
# means higher priority. (As I am checking in order)
for d in dialogue_options:
# A single unmet condition means we discard
# this option and go check the next one.
if d.marriage_required and !character_state.is_married:
continue
if d.time_of_day != TimeOfDay.Any and d.time_of_day != world_state.time_of_day:
continue
if d.affection_threshold > character_state.affection_level:
continue
# If all check passed we chose this dialogue option
return d
push_error("No dialogue option met requirements, consider adding a fallback.")
return null
class WorldState:
var time_of_day: TimeOfDay
I hope I helped you at least somewhat. Good luck in your future game dev journey :).