Custom RichTextEffect: Is there a way to hold data with indivisual CharFXTransform?

Godot Version

4.3

Question

I’m writing a type writer effect, this is what I have so far:

@tool
class_name RichTextTypeWriter
extends RichTextEffect

var bbcode := "type_writer"
signal character_displayed

func _process_custom_fx(char_fx: CharFXTransform) -> bool:
	var delay: float = char_fx.env.get("delay", .5)
	var char_speed: float = char_fx.env.get("char_speed", .2)
	char_fx.color.a = 0
	if char_fx.elapsed_time > delay + char_fx.range.x * char_speed:
		char_fx.color.a = 1
		character_displayed.emit()
	
	return true

The character_displayed signal was to trigger the audio player to make a sound.
Problem with this code is, every single character that passed the check will emit the signal every update.
So the question is, how do I only emit the signal when the character went from 0 to 1?

The first thing I attempted was adding a bool var in the beginning, and flip it inside the check; In the beginning of the function, check for the condition and return false if character had been flip.

var typed: bool = false

func _process_custom_fx(char_fx: CharFXTransform) -> bool:
	if typed:
		return false
	...

Then I learnt that return was for the whole effect resource.
Once it hits false, it just abandon the whole effect and instantly shows everything.

So, I have to figure out how to persist data between calls in character level. How do I do that?
I can’t just assign a bool to existing class, there’s no _ready() to set data only once before process…
I’m out of ideas. Do I just give up on RichEffect and go back to Label.displayed_characters += 1?

Thanks for reading.

So I came across this video, which more or less solve the problem…

@tool
class_name RichTextTypeWriter
extends RichTextEffect

var bbcode := "type_writer"
signal character_displayed(index: int)

func _process_custom_fx(char_fx: CharFXTransform) -> bool:
	var progress:= int(char_fx.elapsed_time / .5)
	if char_fx.relative_index > progress:
		char_fx.color.a = 0.0
	
	if progress != char_fx.env.get("progress", -1):
		char_fx.env["progress"] = progress
		character_displayed.emit(progress)
	
	return true

…Which is just syncing character updating pace with the signal firing.

It more or less works, it keeps firing after the words runs out,
but I just have to make the receiving end check for the sentence length and make comparison before playing the sfx…

…That’s just ugly. I’m keeping the question open for now.