Is it possible to use text codes like the ones in RPG Maker?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By AndreWharn

(Hopefully I’ll be clear enough… I apologize if I won’t, I’m kinda bad at explaining things…)
I was wondering if there’s a way to achieve text codes like these, instead of using BBCode’s:

This is from when my game was made in RPG Maker. Now that I switched engine, I really want to bring this thing again in my game, since I really need it.

The “Commands explanations” part of the image explains how some commands/codes work.
As an example, \p[20] makes wait the text for 20 frames (which is half a second in RPG Maker XP) until the rest appears.
Something like “\f_george_hat_pre” is for indicate which face/portrait to use in the dialogue.
And to stop some effects like italic, I would just type something like \italic and \italic again to stop the effect.

How do I achieve something similiar in Godot?

Oh, and in case you wonder “why don’t you just use BBCode’s?”, it’s because I honestly find it kinda ugly; having to write something like [wait time=seconds] everytime I need a wait and then at the end putting a bunch of [/wait] to close every wait commands I used is really, really unnecessary in my opinion. Also it would get confused really quickly with more stuff, at least for me.

Some more examples of these kind of codes (not from my game this time):
enter image description here

I don’t think theres a way inherently to do this in godot, but I do have two suggestions?

A) You could try using the Dialogic addon (which still uses BBC code… but does have some features built in!)

or

B) Now this is a bit silly, butttt… You could also write a script/addon that converts all your custom text codes into bbc code the engine can use? That way your flow of development isn’t messed up by having to type bbc code! if you’re writing all your dialogue in text files, this could very easily be done with python. Alternatively, I think you could make an in-engine tool script/plugin that’ll do it for you

DigitalDrako | 2022-10-14 18:04

:bust_in_silhouette: Reply From: SnapCracklins

You could make your own functions to do every one of those effects for you?

:bust_in_silhouette: Reply From: zeludtnecniv

I tried something, it use bbcode text so you can continue using text modificator.
You need to write your own action for each tag (end of the script).

this script must be attach to a richtextlabel.

extends RichTextLabel

export var BaseTime = 2;
export var Start = true;

var waitframe = 0;
var waitforinput = false;
var originaltext = "";
var newText = "";
var actionListe = [];
var actionblocktext = false;

# Called when the node enters the scene tree for the first time.
func _ready():
	visible_characters = 0;
	originaltext = bbcode_text
	newText = bbcode_text
	converttag();
	bbcode_text = newText

# search for tag, save actions and remove tag from original text
func converttag():
	# Liste of patterns to find
	var patterns = [
		{name = "p", param = true},
		{name = "w", param = true},
		{name = "ws", param = true},
		{name = "v", param = true},
		{name = "!", param = false}, 
		{name = "\\^", param = false}, 
		{name = "audio_play", param = true}];
	for pat in patterns:
		searchFor(pat)
	
	# Sort action by apparition position in text
	actionListe.sort_custom(self, "comparePosition")
	
	var removed = 0 # store how many char as already be removed
	for action in actionListe:
		newText = newText.substr(0, action.start - removed) + newText.substr(action.end - removed, -1)
		action.position = action.start - removed
		removed += action.end - action.start

# compare two action position
func comparePosition(a, b):
	return a.start < b.start

# use Regex to find an action pattern in the text
# \\(?<actionname>NAME)\[(?<value>[^\]]+)\]
func searchFor(actioninfo):
	var regex = RegEx.new()
	if (actioninfo.param):
		regex.compile("\\\\(?<actionname>" + actioninfo.name + ")\\[(?<value>[^\\]]+)\\]")
		for result in regex.search_all(originaltext):
			actionListe.append({
				start = result.get_start(),
				end = result.get_end(),
				name = result.get_string("actionname"),
				value = result.get_string("value"),
				position = 0 # will be used after to tell the position in the text without tag
				})
	else:
		regex.compile("(\\\\(?<actionname>" + actioninfo.name + "))")
		for result in regex.search_all(originaltext):
			actionListe.append({
				start = result.get_start(),
				end = result.get_end(),
				name = result.get_string("actionname"),
				value = "",
				position = 0 # will be used after to tell the position in the text without tag
				})
		


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
	if (Start && waitframe <= 0 && !waitforinput):
		_ProcessTexte()
	if (waitframe > 0):
		waitframe -= 1    

# check if an action is next step and if text not block, allow showing one more character
func _ProcessTexte():
	actionblocktext = false
	for action in actionListe:
		if (action.position == visible_characters):
			processAction(action)
	if (!actionblocktext):
		visible_characters += 1
		waitframe = BaseTime

# Here you can execute what you want dependings on the action type
func processAction(action):
	match action.name:
		"p":
			print("p action with parameters : " + action.value);
		"ws":
			print("ws action with parameters : " + action.value);
		"v":
			print("v action with parameters : " + action.value);
		"!":
			print("! action without parameters");
		"^":
			print("^ action without parameters");
		"audio_play":
			print("audio_play action with parameters : " + action.value);

omg thank you so much, you really helped me.
I honestly forgot I made this question here, I ended up doing this by my own and it was really similar to your solution, but just, over-complicated and inaccurate, which made me really confused later on what was what.
you kinda saved me from my own mess; I decided to use your method instead since it’s so much better, thank you so much!! I will be sure to credit you for it.

however, sometimes this also seem to produce inaccurate code positions, I have no idea how, but I’ll show you an example of where this has happened: https://youtu.be/7HLxe5jTyPI?t=21

this is a little showcase of the intro of my game, and towards the end, where the dialogue arrives to the phrase “Your car’s extended warranty has been expired from several months.”, the wait code gets executed after showing the letter Y.
however, the original string is:
“I’m so…\p[0.6] happy…\p[0.6]\nto finally being able to contact you…\p[0.6]\nYour car’s extended warranty has been expired from several months.”

does the line breaks maybe mess with the codes?
if you need any other info on maybe how my dialogue system works, I’ll be happy to give it to you!

AndreWharn | 2023-01-08 14:46

Hi, it’s been a while …
Yes i think it’s the line break because my code count “\n” as 2 character but in fact it’s only one. You implement your own line break ? Don’t know how to fix it.

zeludtnecniv | 2023-01-31 11:00