Can a character use a speech voice file not named after the character?

Started by Cassiebsg, Thu 29/08/2019 22:47:18

Previous topic - Next topic

Cassiebsg

I suspect the answer is no, but just in case anyone has any other suggestion other than me creating extra new characters, I'll ask...

I want to be able to have 2 characters, lets say named A and B.

I want to be able to choose which audio file to use when the character speaks.

cA.Say("&12 Hello"); uses by default A12.ogg (or .wav/.mp3)... But I would like to be able to type the same and tell AGS to use AEng or APor instead of A...

Is there anyway to do this via code? Or am I doomed to just create a new character for each instance?

PS - I'm using the new beta, if it makes a difference.
There are those who believe that life here began out there...

Crimson Wizard

One of the ideas for 3.5.0 was to support multiple speech.vox, one per language, but it was not done in time.

Dummy invisible character is one solution and probably used before.

In 3.5.0 you may also use PlayVoiceClip to play a clip associated with any character, but this means you would have to use custom speech functions in your game to combine text with this.

Cassiebsg

Yes, probably easy to just create 2 new characters for each new language then.

Hope that support will come at some point, but for now I'll go with the option of creating new characters. A bit more work, but hopefully it won't mean too much work. :)
Already spent some time trying to code this, and it's almost there... just got one bug I need to find, then the "backbone" should be ready for using...  :-D
There are those who believe that life here began out there...

Cassiebsg

I ran into a "little" problem again about translations... since I'm using 2 translations simultaneous (one character talks one language, the other another). I set it to change the translation acordingly using a custom Say function. And changing characters to get the right voice pack, works good, if some extra work.

My "new" problem is of course that any text on a GUI gets translated as well... and that I did not want.  :-\
so far I have 2 options that I could think of:
1 - Do all GUI text as sprites (lots of work and extra sprites)
2- (suggested my morganw) Use dynamic sprites and render the translated text to them when the language is changed in the settings. A bit of extra coding but should work fine.  :) Likely the option I'll go with unless there's a 3rd option.

Would be nice to be able to do something like:
character.Say(GetTranslation(" txt", translation)); // This would allow me to access the translation file (as an optional feature) and get the translation for that one line.

But, as far as I know this is not possible, right?
There are those who believe that life here began out there...

Crimson Wizard

Quote from: Cassiebsg on Sat 31/08/2019 15:39:56
I ran into a "little" problem again about translations... since I'm using 2 translations simultaneous (one character talks one language, the other another).

Could you explain why do you need to use whole separate translation for this? If it's certain character's lines, could not they be as well added to same translation file?

Cassiebsg

Well, cause I only want to use one translation file for each language. If I make the game with 2 different languages, then whom ever wants to translate will have a problem with the lines (and translate 2 or more files instead of one). Besides I rather code everything in english, so I don't have to keep changing "mind" set.

(as it is right now, I can have English-English; English-Portuguese, English-Danish, Pt-Eng, Pt-Pt, PT-DK, DK-Eng, DK-PT and DK-DK (with 3 translation files)...)
If I do only one file for PT-DK, then I would need to make a DK-PT, and so on (one file for each possible languages choices)

As I said before, maybe was on IRC, I could save me the trouble of getting the game ready for easy translation, and just do it for my own purpose, which is to create a game to help my son learn Portuguese. Then I would have no problems coding... or translating.  ;)
I just thought it would be nice to be able to add other languages to it, and thus everyone could use it to learn whichever language is available.  ;-D
There are those who believe that life here began out there...

Crimson Wizard

Hmm, I dont think this is a best approach with AGS currently as you cannot make separate TRS file only for one character or only for a group of strings.
And there's no way to choose a combination of translation files, at least not with built-in means.

Have you considered scripting your own translation for this purpose instead? You could have a separate txt file for this particular character, read lines from there and use custom function similar to GetTranslation when displaying that character's lines.

Cassiebsg

I would consider anything, as long as it works.  :-D

But keep in mind that I'm not a coder, the only scripting languages I know are COBOL (which I learned way back in 91, and never used it) and AGS (and even AGS, I consider my self still as basic/beginner coder)  (roll).
So, in other words, I would have no idea how to code that custom function.
There are those who believe that life here began out there...

Crimson Wizard

There is another possibility, but it requires to change the engine.

In 3.5.0 we've introduced new script types called Dictionary which stores pairs of strings (key/value). I thought that in theory we could unify number of now separate features in script by replacing their existing functions with this Dictionary.
Translation is one of such features, because this is just how translation works in AGS: it stores a dictionary of strings (where key is line in original language and value is line in other language).

So my thinking was, if we expose translation dictionary in script using new Dictionary type, that may allow users to freely edit translations in script. This would also allow to replace particular group of lines with something different, and so on.

Maybe this could be the way to go in the long run.

Cassiebsg

For now I'll just use morganws suggestion about using dynamic sprites. It's a bit more work, but once it's in place should work fine.  (nod)
There are those who believe that life here began out there...

Crimson Wizard

Quote from: Cassiebsg on Sat 31/08/2019 19:00:26
For now I'll just use morganws suggestion about using dynamic sprites. It's a bit more work, but once it's in place should work fine.  (nod)

You could also store strings themselves and display them as custom speech too (that prevents ags from trying to translate already translated lines). If you have a regular speech style that should be relatively easy to emulate. If you have a custom speech already then it's even simplier.

Cassiebsg

What? Sorry, my brain isn't quite following up.
What do you mean and how?
There are those who believe that life here began out there...

Crimson Wizard

I thought that if you are going to do this for character speech only, you may store translation text in a string, and display that string with Say, instead of a dynamic sprite with text on it.

But then I realized that Character.Say translates the text automatically. So even character.Say(GetTranslation(" txt", translation)); won't work correctly if it existed!

To do this with bare text, ags would need either some way to prevent automatic translation in Say and similar functions, or GUI labels (if you are using these for custom speech).

Actually, should be possible to add a property to gui Label to tell it not to translate its text (again, this has to be implemented in engine first), similar to how ListBox has a Translated property.

Cassiebsg

Well, if you can add an option to avoid labels and button texts (not really a must here, since I could just add a label over the button "image".  ;) ) that would be great.  (nod)

Right now what I have is selecting at game start the two languages via a GUI.
This sets a variable for each language and changes the characters (which are also set to a character variable) so I can use voices.

For character speech, I created a simple function with only 2 lines... the first checks if the current translation is different from the language variable I want to use, if it is, it changes the translation to the desired one... and then the Say line.

Works pretty good :)
(Maybe I should post my code, for anyone else looking to solve this problem...)

EDIT: Here's the current code:
Code: ags


function SayLearn(String txt)
{
	if (whatLingoLearn!=Game.TranslationFilename) Game.ChangeTranslation(whatLingoLearn); // whatLingoLearn is a global String variable
	whatLearn.Say(txt); // whatLearn is a global character variable
}

function SayKnow(String txt)
{
	if (whatLingoKnow!=Game.TranslationFilename) Game.ChangeTranslation(whatLingoKnow);
	whatKnown.Say(txt);
}

function game_start() 
{
	if (Game.TranslationFilename!=whatLingoKnow) Game.ChangeTranslation(whatLingoKnow); // in case the language has been changed before or via winsetup (I may change this, but for know I want it to always start at English as default)
	whatKnown=cKEng; // setting the default cause for some reason I can't set the default on Global
	whatLearn=cLEng;
	show_LingoKnow_list();
	show_LingoLearn_list(); //just the function to populate the text listbox (not posted here, as it isn't relevant)
}

function lbLingoLearn_OnSelectionChanged(GUIControl *control) // Code for the selection language to learn GUI ListBox
{
	String selectedTranslation = lbLingoLearn.Items[lbLingoLearn.SelectedIndex];
	if (selectedTranslation=="English") 
	{
		if (whatLearn!=cLEng)  //  cLEng is the english speaking character for the language to learn 
		{
		cLEng.ChangeRoom(whatLearn.Room, whatLearn.x, whatLearn.y, whatLearn.Loop);
		cLEng.SetAsPlayer();
		whatLearn.ChangeRoom(-1);
		whatLearn=cLEng;
		}		
	}
	else if (selectedTranslation=="Portuguese") 
	{
		if (whatLearn!=cLPor)
		{
		cLPor.ChangeRoom(whatLearn.Room, whatLearn.x, whatLearn.y, whatLearn.Loop);
		cLPor.SetAsPlayer();
		whatLearn.ChangeRoom(-1);
		whatLearn=cLPor;
		}
	} 
	else if (selectedTranslation=="Danish") 
	{
		if (whatLearn!=cLDan)
		{
		cLDan.ChangeRoom(whatLearn.Room, whatLearn.x, whatLearn.y, whatLearn.Loop);
		cLDan.SetAsPlayer();
		whatLearn.ChangeRoom(-1);
		whatLearn=cLDan;
		}
	} 
	else Display("Unable to change the translation");
	whatLingoLearn=selectedTranslation;
	selectedTranslation="";
}

function lbLingoKnow_OnSelectionChanged(GUIControl *control)  // Code for the selection language of the known language GUI ListBox
{
	String selectedTranslation = lbLingoKnow.Items[lbLingoKnow.SelectedIndex];
	if (selectedTranslation=="English") 
	{
		if (whatKnown!=cKEng)  //  cKEng is the english speaking character for the knows language 
		{
		Game.ChangeTranslation("English");
		cKEng.ChangeRoom(whatKnown.Room, whatKnown.x, whatKnown.y, whatKnown.Loop);
		cKEng.SetAsPlayer();
		whatKnown.ChangeRoom(-1);
		whatKnown=cKEng;
		}		
	}
	else if (selectedTranslation=="Portuguese") 
	{
		if (whatKnown!=cKPor)
		{
		Game.ChangeTranslation("Portuguese");
		cKPor.ChangeRoom(whatKnown.Room, whatKnown.x, whatKnown.y, whatKnown.Loop);
		cKPor.SetAsPlayer();
		whatKnown.ChangeRoom(-1);
		whatKnown=cKPor;
		}
	} 
	else if (selectedTranslation=="Danish") 
	{
		if (whatKnown!=cKDan)
		{
		Game.ChangeTranslation("Danish");
		cKDan.ChangeRoom(whatKnown.Room, whatKnown.x, whatKnown.y, whatKnown.Loop);
		cKDan.SetAsPlayer();
		whatKnown.ChangeRoom(-1);
		whatKnown=cKDan;
		}
	} 
	else Display("Unable to change the translation");
	whatLingoKnow=selectedTranslation;
	selectedTranslation="";
}




EDIT2: I just realized that I probably only need to use 1 character (the help character) to change translation, while the all eventualt others can just use the default translation.   ;) I just need to change the translation back, after the character says it's line.
There are those who believe that life here began out there...

TheManInBoots

I don't know if I got all the information right and if I understood the whole problem, but would using global bool variables add some helpful flexibility?

For example you have a bool for every language:

(bool beng, bool bpor, bool bfre...)

Then whenever a character says something at any point you always write as follows:

if(beng==true)
{character.Say("&3 I'm hungry!");}

else if(bpor==true)
{character.Say("Eu estou com fome!");
//or: Por.Say("&3 Eu estou com fome!");
}

else if(bfre==true)
{character.Say("J'ai faim!");
//or: Fre.Say("&3 J'ai faim!");
}


This gives you way more flexibility. You can change the language at any time and at any point in the script.

And if you want to change the language for a certain character separately, let's say for "Fred" for example, you can add another bool group:
(bool bfredeng, bool bfredpor, bool bfredfre...)

And whenever "Fred" is talking you script with the according bool variables:

f(bfredeng==true)
{cFred.Say("&3 I'm hungry!");}

else if(bfredpor==true)
{cFred.Say("Eu estou com fome!"); }

else if(bfredfre==true)
{cFred.Say("J'ai faim!");}

So then when you want to change the language for all the characters (by clicking a button or anything else) you simply change all of the bools, e.g.:

bpor=false;
bfre=false;
beng=true;

bfredpor=false;
bfredfre=false;
bfredeng=true;

...

which will still give you the ability to change the language for only one character at any point in the game.


Also, do you need different audio groups and names for every language?
Could you not simply, if your character speaks for example 40 times during the game, and so you have 40 audio files for every language, script it like this and add every language under the same speech group name:

if(beng==true)
{character.Say("&3 I'm hungry!");}

else if(bpor==true)
{character.Say("&43 Eu estou com fome!");
}

else
{character.Say("&83 J'ai faim!");}

That was my idea...

Cassiebsg

That would be a nightmare to code, maintain and add translations.
Yes it would work, but my code above also works just fine and the point is to make it easy to add new translations, not make it complicated with all those bools.

But thanks for trying.  (nod)
There are those who believe that life here began out there...

Khris

In general, one wouldn't use three bools to store a character's language but a single int (set to 0, 1 or 2).
Multiple bools would only be used for game states that are independent of each other, like for instance being rested/exhausted and being thirsty/not thirsty.

TheManInBoots

Alright, no worries, I wanted to try. ^^ That's interesting, it's true, an int is less messy.

I had just another idea for the naming of the audio files. I would have actually just categorized the languages by having one language numbered from 0 to 100, the other language 200 to 300, the other 300 to 400, so that audio file &043 in English would be the same thing audio file &143 in Portuguese for example, and &243 in French...you just change the first digit to change the language, this way you have a nice overview and don't necessarily need a different character for a different language. (Obviously if you have more than 100 files you make the gap between the languages 1000, not 100) Also you can easily add and number a new language without a problem!
It's pretty obvious but you get the gist! ;)

Cassiebsg

Okay, was thinking about this, and wondering if adding support for custom speech folders would be easier to add?

So that one could change the speech folder via code?
That way one could add: .../game/speech/Eng ;  .../game/speech/Por ;  .../game/speech/Dan ; etc. and inside the folder would be a speech.vox file (so no need to change how the game handles speech.vox file, just where it looks for it.)

Command wise, all one would need, would be a command to change the folder path via code, just like I'm changing the translation.

If you think it's easier to implement as a temporary step, until full implementation is possible, then I'll be glad to add it to the suggestions, otherwise I'll shut-up and use what is possible at this time.  ;)
There are those who believe that life here began out there...

Crimson Wizard

Quote from: Cassiebsg on Wed 18/09/2019 00:40:41
Okay, was thinking about this, and wondering if adding support for custom speech folders would be easier to add?

3.5.0 is done and I am not adding anything there, so this will have to be postponed since next development stage anyway.

BTW, my thinking was to have speech folders in the project, and separate speech vox files in compiled game (not in different folders), named using some convention like sp_<translationname>.vox.

By default game will switch to another vox automatically at ChangeTranslation if it exists. I guess since there's a use case for using separate text/voiceover then there could be a new function to choose separate vox, but this has to be thought through.


Here's a big ticket I wrote some time ago:
https://github.com/adventuregamestudio/ags/issues/686

SMF spam blocked by CleanTalk