Making text appear only once in Dialogue manager

Godot V 4.3

Question:
I am having trouble with this dialogue manager extension for my godot game. The game itself has the player walking around and talking to NPC’s now if the player has already talked to this NPC it will choose “for now” randomly 1 of the other dialogues to give to the player. However these dialogues can repeat. I DO NOT WANT THIS. I want the manager to never repeat the same dialogue and if the player runs out of dialogue the NPC just says “that’s all the time I have” how do I do this using dialogue manager. Here is what I have so far

~ start
if not GameState.has_met_accountant
set GameState.has_met_accountant = true

Accountant: "...Oh. Hey. You're...uh...you. Right. Guess I should introduce myself."
Accountant: "I'm {{GameState.npc_name}}. I do accounting. Not by choice."
Accountant: "Anyway. What do you want?"

- Just saying hi.
	Accountant: "Cool. Hi. Can I, uh, go back to doing nothing now?"
- You don't seem too excited to be here.
	Accountant: "Oh, no, I'm [wave amp=25 freq=5]thrilled.[/wave] This is exactly how I wanted to spend my evening. Woooo. Party."
	Accountant: "Look, if the numbers aren't on fire, I usually don't care."
- How's the accounting life?
	Accountant: "Exciting. Riveting. Absolutely life-changing. Yesterday, I balanced a spreadsheet. The suspense was unreal."

Accountant: "Anyway. If you need someone to make small talk with, maybe find someone who *wants* to be here."
=> END

else
%
Accountant: “…Oh. You again. What now?”
- Just thought I’d check in on my favorite accountant.
Accountant: “Favorite? Yikes. If I’m your favorite, you need better taste in accountants.”
- You look like you’re having fun.
Accountant: “I look like I’m awake. That’s about it.”
- I already forgot your name.
Accountant: “Figures. It’s {{GameState.npc_name}}. But, hey, I’ll probably forget yours too, so we’re even.”
Accountant: “Alright. That’s enough social interaction for today. See ya.”
=> END

% 
	Accountant: "Oh wow, it’s you. Again. What a surprise."
	- Enjoying the party yet?
		Accountant: "Oh yeah. Totally. I'm having the time of my life. Woohoo. Party animal."
	- You ever smile?
		Accountant: "[speed=0.05]...[/speed] That was it. You missed it."
	- So, crunched any numbers today?
		Accountant: "Yeah, I calculated the exact amount of patience I have left. Wanna guess?"
	Accountant: "Alright, well, you’ve used up today’s conversation quota. Bye."
	=> END

You have access to global scripts. I don’t think there’s anything from stopping you doing something like this:

# GameState.gd
var random_dialogue_ids : Array[int] = [1, 2, 3]

func get_random_dialogue_id()->int:
	var id : int = -1
	if random_dialogue_ids.size() > 0:
		randi_range(0, random_dialogue_ids.size())
		random_dialogue_ids.erase(id)
	return id

Then in Dialogue Manager, check the function’s return value. The function will always return a new number, until the array is empty. If the array is empty, the return value is -1. This indicates there is now more new random dialogue to show.

This creates a massive dependency between your dialogue script and GameState script, so use with care and maybe make the solution a bit more robust.

This doesn’t really work this is my GameState code and I don’t know how to add what I need to add please help extends Node

var has_met_manager: bool = false
var has_met_accountant: bool = false
var npc_name: String = “”
var assigned_roles: Array = # Keep track of which roles are already assigned

Store all available random dialogues by ID

var random_dialogue_ids: Array[int] =
var used_dialogue_ids: Array[int] = # Track which dialogues have been used

const MALE_NAMES = [“James”, “John”, “Michael”, “William”, “David”]
const FEMALE_NAMES = [“Mary”, “Patricia”, “Jennifer”, “Linda”, “Elizabeth”]
const MALE_HAIR_STYLES = [
“res://Assets/Characters/NPC/Hair/male/Hair 1M (black).png”,
“res://Assets/Characters/NPC/Hair/male/Hair 1M (brown).png”
]
const FEMALE_HAIR_STYLES = [
“res://Assets/Characters/NPC/Hair/female/Hair 1 (gold).png”,
“res://Assets/Characters/NPC/Hair/female/Hair 1 (grey).png”
]
const ROLES = {
“accountant”: {
“dialogue_file”: “res://Assets/UI/Dialogues/Accountant.dialogue”,
},
“manager”: {
“dialogue_file”: “res://Assets/UI/Dialogues/Manager.dialogue”,
}
}

func _ready():
assigned_roles.clear() # Clear assigned roles when game starts
reset_dialogue_pool() # Initialize dialogue pool

func reset_dialogue_pool() → void:
# Reset available dialogues - add any new dialogue IDs here
random_dialogue_ids = [1, 2, 3]
used_dialogue_ids.clear()

func get_random_dialogue_id() → int:
# If all dialogues have been used
if random_dialogue_ids.is_empty():
return -1

# Pick a random dialogue from remaining ones
var index = randi() % random_dialogue_ids.size()
var id = random_dialogue_ids[index]

# Move the dialogue ID from available to used
random_dialogue_ids.remove_at(index)
used_dialogue_ids.append(id)

return id

func get_random_name(gender_enum: int) → String:
var names = MALE_NAMES if gender_enum == 0 else FEMALE_NAMES
return names[randi() % names.size()]

func get_random_hair_style(gender_enum: int) → String:
var styles = MALE_HAIR_STYLES if gender_enum == 0 else FEMALE_HAIR_STYLES
return styles[randi() % styles.size()]

func get_available_role() → String:
# Get all roles that haven’t been assigned yet
var available_roles =
for role in ROLES.keys():
if role not in assigned_roles:
available_roles.append(role)

# If no roles are available, print an error (this shouldn't happen if NPCs are properly counted)
if available_roles.is_empty():
	push_error("No more roles available! This shouldn't happen!")
	return "accountant"

# Randomly select from available roles
var selected_role = available_roles[randi() % available_roles.size()]
assigned_roles.append(selected_role)
return selected_role

func release_role(role: String) → void:
# Remove role from assigned_roles when an NPC is freed
if role in assigned_roles:
assigned_roles.erase(role)

func get_role_data(role: String) → Dictionary:
return ROLES[role] if role in ROLES else ROLES[“accountant”]

Can you describe in what way it doesn’t work? That would be quite helpful.

Did you change the dialogue script so it uses the new function? i.e. like this:

~ start
Speaker: Line 1.
Speaker: Line 2.
if GameState.get_random_dialogue_id() == 1:
	Speaker: Line 3.
if GameState.get_random_dialogue_id() == -1:
	Speaker: I have nothing more to say.
=> END

edit: you actually need to call get_random_dialogue_id once at the start of your script, store the return value in a variable, then use that variable to test the if-statements. If you don’t, an element will be removed from the array every time the function is called.

yes I did put that but does not make it randomize the dialouge for some reason and it comes back as a syntax error. Sorry for not being specific I have never really posted something on the godot forums before :frowning:

Can you specify the syntax error? I tried on my end and I can call functions just fine.

it says this in the dialouge manager script Assertion failed: “dialogue_id” not found. States with directly referenceable properties/methods/signals include “{ “self”: }”, “ExampleBalloon”, “Office”. Autoloads need to be referenced by their name to use their properties. and this is my actual dialouge ~ start
if not GameState.has_met_accountant:
set GameState.has_met_accountant = true
Accountant: “…Oh. Hey. You’re…uh…you. Right. Guess I should introduce myself.”
Accountant: “I’m {{GameState.npc_name}}. I do accounting. Not by choice.”
Accountant: “Anyway. What do you want?”
- Just saying hi.
Accountant: “Cool. Hi. Can I, uh, go back to doing nothing now?”
- You don’t seem too excited to be here.
Accountant: “Oh, no, I’m [wave amp=25 freq=5]thrilled.[/wave] This is exactly how I wanted to spend my evening. Woooo. Party.”
Accountant: “Look, if the numbers aren’t on fire, I usually don’t care.”
- How’s the accounting life?
Accountant: “Exciting. Riveting. Absolutely life-changing. Yesterday, I balanced a spreadsheet. The suspense was unreal.”
Accountant: “Anyway. If you need someone to make small talk with, maybe find someone who wants to be here.”
=> END
else
set dialogue_id = GameState.get_random_dialogue_id()
if dialogue_id == 1:
Accountant: “…Oh. You again.”
- Hi, {{GameState.npc_name}}.
Accountant: “Huh. You actually remembered. Points for you, I guess.”
- Hi, uh… Jessica?
Accountant: “Jessica? No. But sure, let’s go with that.”
- Hey, accounting guy.
Accountant: “Wow. That’s me. ‘Accounting guy.’ Love it.”
Accountant: “Anyway. Great talk. I’m going back to standing here.”
=> END
if dialogue_id == 2:
Accountant: “oh hello how are you”
=> END
if dialogue_id == 3:
Accountant: “UHHHH what do you want now”
=> END
if dialogue_id == -1:
Accountant: “I have nothing more to say.”
=> END

The error happens because Dialogue Manager itself is stateless. i.e. it needs a different script to store state in. In GameState, add @export var dialogue_id : int = -1, then replace set dialogue_id = GameState.get_random_dialogue_id() in your dialogue script with set GameState.dialogue_id = GameState.get_random_dialogue_id().

MAN THANK YOU SO MUCH… This is for a school project and I really

needed it to work THANK YOU

1 Like