Jump to content
Jezuro

New conversation system how-to

Recommended Posts

As many of you have noticed, Arma II introduced a new conversation system (I'll be referring to it as "conversations" further on) which replaced the old say/...Radio description.ext-based approach. There have been many questions regarding this subject, so I decided to write a short overview of the possibilities and usage of this system.

Main advantages

  • The system itself waits for the sound sample to finish. No more sample time measuring and countless sleeps!
  • Conversation always flows through the appropriate channel (direct for face to face, team radio for distant team members etc.)
  • Possibility to create fully dynamic conversations.
  • Transparent syntax (FSM branches allow fast and efficient edits).

Theory

In order to run a conversation, all of the participants need to have the proper topic added to them. The conversation then starts with the first sentence which one of the participants says to another. After that, the conversation flow is usually controlled by scripts - FSM's and event handlers assigned to the participants. After a participant receives a sentence, his script reacts to its ID and reacts with the proper answer. It may sound a bit complicated, but it's really not.

Let's start with adding the topic. The scripting command to do this is kbAddTopic:

person kbAddTopic [topicName, fileName.bikb, (fileName.fsm, (eventHandler))]

  • topicName: (string) Can be anything.
  • fileName.bikb: (string) Name of the .bikb file - will be described later.
  • fileName.fsm: (string) Name of the .fsm file - will be described later. Optional.
  • eventHandler: (string or code) Will be described later. Optional.

Here is an example of what the code usually looks like:

unit2 kbAddTopic ["baseDefended", "baseDefended.bikb", "baseDefended_unit2.fsm", {call compile preprocessFileLineNumbers "baseDefended_unit2.sqf"}]

.bikb files

Stand for "Bohemia Interactive Knowledge Base" as it was originally used only for storing an AI unit's memory of what it has seen. Not important to you. You will use these files to store all the speech samples (referred to as "Sentences" further on) used for the topic. This is in fact quite similar to the good old cfgSounds and cfgRadio classes in description.ext. Here is a short .bikb file:

class Sentences
{

   class example_01
   {
       text = "Hello Bret.";
       speech[] = {"\Sound\jemaine01.ogg"};
       class Arguments {};

   };

   class example_02
   {
       text = "Oh, hello Jemaine.";
       speech[] = {"\Sound\bret01.ogg"};
       class Arguments {};

   }
};
class Arguments{};
class Special{};
startWithVocal[] = {hour};
startWithConsonant[] = {europe, university}

  • text: Subtitles used to caption the sample. If you're using stringtable, use $stringName (has to start with "STR_"!)
  • speech: Sound sample.
  • arguments, special, startWithVocal, startWithConsonant: Don't worry about these.

The scripts

In both FSM's and event handlers, there are some default variables which you'll be using quite often:

  • _this: Receiver of the sentence. The unit that had this particular script assigned via kbAddTopic.
  • _from: Self-explanatory. Basically the unit that told me the sentence.
  • _sentenceId: The sentence this unit is reacting to. Defined in .bikb in class Sentences.
  • _topic: Self-explanatory. Topic name used in kbAddTopic.

FSM's

FSM stands for Finite-state machine. We have released the FSM Editor some time ago. I will not describe its functionality here, I'm sure there are plenty tutorials out there. You can open one of the conversation FSMs used in the example mission (you need to have the FSM Editor installed to open it).

As you can see, the FSM parameter is optional in kbAddTopic. That is because a) you don't want to use scripts at all and rather manage the conversation manually (about that later) or b) the unit is always controlled by a player. The engine recognizes who's controlling the unit by the time it receives a sentence. If it's controlled by AI, the assigned FSM is executed. If it's player-controlled, it fires the event handler. If you're making an MP mission and the unit is playable, you will want to use both the FSM and the event handler.

Event Handlers

Nothing to do with our standard event handlers. This is a code used only if the unit is controlled by a player and is executed not only if it receives a sentence, but also if the player points at someone and is close enough to start a conversation ("Talk to" action appears). This is very important to understand. FSM's are executed only once after each received sentence, event handlers are fired constantly (usually every frame) as long as you're pointing at somebody.

Here is an example showing all you might want to use (you should get familiar with kbTell and kbWasSaid first):

// here we'll be storing all the sentences from which the player will choose (the menu on the left side of the screen)
// if there's only one option in the array, you will have the sentence as the "Talk to" action

BIS_convMenu = [];

// we want the player to be able to approach his buddy and talk to him via the action menu
// we need to check:
// if I'm pointing at my buddy
// if I'm not answering any of his sentences
// if I haven't told him hello already
// then we add that array to BIS_convMenu - the parameters are mostly self-explanatory

if (_from == buddy1 && _sentenceId == "" && !(_this kbWasSaid [_from, _topic, "hello1", 999999])) then {
   BIS_convMenu = BIS_convMenu + [["Say hello.", _topic, "hello1", []]]
};

// here we make the unit say the proper sentence based on the one he just received
// I use switch-case-do, it's completely up to you how to evaluate it (if-then etc.)

switch (_sentenceId) do
{
   case "hello1": {
       _this kbTell [_from, _topic, "hi_how_are_you"]
   };
   case "good_you": {
       _this kbTell [_from, _topic, "fine_thanks"]
   };
   case "what_do_we_do_today": {
       // here the player will have 3 answers to choose from
       BIS_convMenu = BIS_convMenu + [["Football.", _topic, "choose_footbal", []]];
       BIS_convMenu = BIS_convMenu + [["Bike.", _topic, "choose_bike", []]];
       BIS_convMenu = BIS_convMenu + [["Arma II.", _topic, "choose_arma2", []]]
   };
};

// return the sentence list pool
BIS_convMenu

There. Everything should be explained in the comments inside the code. As you can see, it's nothing more than a compiled sqf function.

"Interrupted" event

The left-side menu on the HUD with the list of possible sentences can be closed via backspace at all times. If you want to handle this event as well, you just have to add new sentence class called Interrupted into you .bikb file. It can be then used as a standard _sentenceId in the script.

Example mission

And finally, here it is. The example mission contains everything that has been explained here and shows you mainly how to manage the files. It contains voice samples from the Harvest Red campaign, please don't be surprised that the conversation is a bit out of context :)

Get it here.

"Manual" conversation flow

For some reasons, you may not want to use FSMs and event handlers to control a conversation. For this, you will want to use kbWasSaid. An example will suffice I think.

miles kbAddTopic ["briefing", "kb\briefing.bikb", ""];
shaftoe kbAddTopic ["briefing", "kb\briefing.bikb", ""];

shaftoe kbTell [miles, "briefing", "shaftoe_briefing_H_1"];
waitUntil {shaftoe kbWasSaid [miles, "briefing", "shaftoe_briefing_H_1", 3]};

miles kbTell [shaftoe, "briefing", "shaftoe_briefing_M_1"];
waitUntil {miles kbWasSaid [shaftoe, "briefing", "shaftoe_briefing_M_1", 3]};

hint "Conversation ended."

Mission accomplished

:yay:

Well, I think that's everything for now. It's quite possible I forgot to mention something or made a typo in the scripts, please let me know if you're confused about something and I'll edit it out.

Edited by W0lle
Missing "\" in speech paths
  • Thanks 1

Share this post


Link to post
Share on other sites

Finally! Thanks man! :yay:

But it's a bit late, i already use the old approach. Anyway, i think i'll try it out in future missions.

Edith: I somehow noticed in earlier tries that there's a problem with custiom sounds. They weren't working. Is there anything i must know to use custom sounds?

Share this post


Link to post
Share on other sites

Excellent article and great to see this kind of information coming from BIS - thank you !

Share this post


Link to post
Share on other sites

Excellent article! I especially loved the reference to Flight of the Conchords. :D

Edited by MadDogX

Share this post


Link to post
Share on other sites

I would be nice if someone could provide a very easy Example Conversation Mission cause i dont know where i add the Topics and which files are exactly needed.

I tried some things out now, but no success. Would be much appreciated!

Edited by Himmelsfeuer

Share this post


Link to post
Share on other sites

Currently it's not possible to use sound files stored in the mission directory itself for the new system of conversations (the path in the bikb files starts in the addons folder). I consulted this with our programmers and they kindly made a quick hotfix. Still waiting for the commit though, I'll prepare an example mission as soon as this fix is released.

Share this post


Link to post
Share on other sites

Great! That is what i call sercvice! Thank you and your programmers!

Share this post


Link to post
Share on other sites

The fix will be present in the upcoming patch 1.05. I edited the first post with the Interrupted event and the example mission (note that you won't be able to hear the voice samples before updating to v1.05).

Share this post


Link to post
Share on other sites

Thanks man! I just finished my first conversation with multiple choice! :yay:

Share this post


Link to post
Share on other sites

Thanks for the example mission !

Hope i get it now...:)

I wanted to try it without Speechsamples first. But i dont get where you create the Subtitles[Texts only] for Speech Files.

Talking about this

text = "$STR_shaftoe_briefing_m_0"; [ where is that File???]

speech[] = {"\ca\dubbing\C0_FirstToFight\shaftoe_briefing\shaftoe_briefing_M_0.ogg"};

Edited by Himmelsfeuer

Share this post


Link to post
Share on other sites

The strings I used are defined in the core stringtable. The engine looks there if it can't find the string ID in the mission's stringtable.

Share this post


Link to post
Share on other sites

Thanks very much for the tutorial, its not really easy with the FSM, but if you get behind it, you can do it with some work and without much scriptingknowledge....still i need yours now;-)

Is it possible to use the FSM Conditionfield with waypoints and triggers?

I want to start a AI Action if you has chosen a specific Topic Answer, how would you do it? Thanks!

Share this post


Link to post
Share on other sites

Hm, i think that's possible. I'd use variables. So a var is set to true if you chose a specific answer. Then you need a trigger wich checks for the variable.

Share this post


Link to post
Share on other sites

Argh, i've got a situation here wich is driving me mad!

I just want to create a little briefing where two people telling the player what's going on and the player can ask a few questions. But it doesns't work at all! I made a small test mission a few days ago to learn how to create such conversations with success. And now i did the same things as in my test mission, only in a bigger dimension but - no success! There's no error in the .rpt, so i got no clue what it might be. But i have the suspicion that the FSMs aren't triggered at all. Anyway, here're the basic files i use:

"init.sqf"

// Testing options - for testing purposes only
teleport = doe addAction ["Teleport","scripts\teleport.sqf"];
{_x allowDamage false} forEach [doe,romanov,bulgarin,stark];

// Ladislaus Ruznik Variable
if (isNil "aliveRuznik") then {aliveRuznik = true};

// Weather
0 setOvercast 0.5;

// Taskhint - Briefing
mk_fTaskHint = compile (preprocessFileLineNumbers "tasks\fTaskHint.sqf");
nil = [] execVM "tasks\briefing.sqf";

// Urban Patrol Script
// UPS = compile (preprocessFileLineNumbers "scripts\UPS.sqf");

// Film Grain
"colorCorrections" ppEffectAdjust [1, 1, -0.003, [0.0, 0.0, 0.0, 0.0], [0.9, 0.9, 1, 0.4],  [0.199, 0.587, 0.114, 0.0]];  
"colorCorrections" ppEffectCommit 0;  
"colorCorrections" ppEffectEnable true;
//light film grain
"filmGrain" ppEffectEnable true; 
"filmGrain" ppEffectAdjust [0.03, 1, 1, 0.1, 1, false];
"filmGrain" ppEffectCommit 0;

// First Scene 
nil = [] execVM "scenes\start.sqf";

"start.sqf"

sleep 0.1;
player kbAddTopic ["start", "scenes\start.bikb", "", {call compile preprocessFileLineNumbers "scenes\startDoe.sqf"}];
faustin kbAddTopic ["start", "scenes\start.bikb", "scenes\startFaustin.fsm"];
rascalov kbAddTopic ["start", "scenes\start.bikb", "scenes\startRascalov.fsm"];
showHUD false;
{_x setVariable ["BIS_noCoreConversations", true]} forEach [faustin,rascalov,bulgarin,romanov,stark,doe];
{_x disableAI "anim"} forEach [faustin,rascalov,bulgarin,romanov,stark,doe];
doe switchMove "C4_briefing_rodriguez";
romanov switchMove "C4_briefing_ohara";
stark switchMove "C4_briefing_cooper";
bulgarin switchMove "rodriguez_c0briefing";
faustin playMove "AidlPercSnonWnonDnon_talkBS";
rascalov playMove "AmovPercMstpSlowWrflDnon_talking";
cutText ["", "BLACK IN", 7];
// sleep 7;
// rascalov kbTell [doe,"start","start_rascalov_1"];
waitUntil {rascalov kbWasSaid [doe, "start", "start_rascalov_6", 999999]};
sleep 3;
hint "Debug!";

"startDoe.sqf"

BIS_convMenu = [];

if (!(rascalov kbWasSaid [doe, _topic, "start_rascalov_1", 999999])) then {rascalov kbTell [doe, _topic, "start_rascalov_1"]};

switch (_sentenceId) do {

case "start_rascalov_2": {
	 BIS_convMenu = BIS_convMenu + [["So we shall check it out?", _topic, "start_doe_1a", []]];
	 BIS_convMenu = BIS_convMenu + [["Fuck off, Dimitri!", _topic, "start_doe_1b", []]];
};

case "start_faustin_4": {
	 BIS_convMenu = BIS_convMenu + [["What's the content of the message?", _topic, "start_doe_2a", []]];
	 BIS_convMenu = BIS_convMenu + [["Where's the exact source of the transmission?", _topic, "start_doe_2b", []]];
	 BIS_convMenu = BIS_convMenu + [["How do we get there?", _topic, "start_doe_2c", []]];
	 BIS_convMenu = BIS_convMenu + [["Any hostile activity?", _topic, "start_doe_2d", []]];
	 BIS_convMenu = BIS_convMenu + [["I've got everything i need. Let's go.", _topic, "start_doe_3", []]];
};

case "start_faustin_5a": {
	 BIS_convMenu = BIS_convMenu + [["What's the content of the message?", _topic, "start_doe_2a", []]];
	 BIS_convMenu = BIS_convMenu + [["Where's the exact source of the transmission?", _topic, "start_doe_2b", []]];
	 BIS_convMenu = BIS_convMenu + [["How do we get there?", _topic, "start_doe_2c", []]];
	 BIS_convMenu = BIS_convMenu + [["Any hostile activity?", _topic, "start_doe_2d", []]];
	 BIS_convMenu = BIS_convMenu + [["I've got everything i need. Let's go.", _topic, "start_doe_3", []]];
};

case "start_faustin_5b": {
	 BIS_convMenu = BIS_convMenu + [["What's the content of the message?", _topic, "start_doe_2a", []]];
	 BIS_convMenu = BIS_convMenu + [["Where's the exact source of the transmission?", _topic, "start_doe_2b", []]];
	 BIS_convMenu = BIS_convMenu + [["How do we get there?", _topic, "start_doe_2c", []]];
	 BIS_convMenu = BIS_convMenu + [["Any hostile activity?", _topic, "start_doe_2d", []]];
	 BIS_convMenu = BIS_convMenu + [["I've got everything i need. Let's go.", _topic, "start_doe_3", []]];
};

case "start_faustin_5ca": {
	 BIS_convMenu = BIS_convMenu + [["What's the content of the message?", _topic, "start_doe_2a", []]];
	 BIS_convMenu = BIS_convMenu + [["Where's the exact source of the transmission?", _topic, "start_doe_2b", []]];
	 BIS_convMenu = BIS_convMenu + [["How do we get there?", _topic, "start_doe_2c", []]];
	 BIS_convMenu = BIS_convMenu + [["Any hostile activity?", _topic, "start_doe_2d", []]];
	 BIS_convMenu = BIS_convMenu + [["I've got everything i need. Let's go.", _topic, "start_doe_3", []]];
};

case "start_rascalov_4": {
	 BIS_convMenu = BIS_convMenu + [["I'm sorry for your loss.", _topic, "start_doe_2a", []]];
	 BIS_convMenu = BIS_convMenu + [["Hey, it wasn't my fault asshole!", _topic, "start_doe_2b", []]];
};

case "start_faustin_5cb": {
	 BIS_convMenu = BIS_convMenu + [["What's the content of the message?", _topic, "start_doe_2a", []]];
	 BIS_convMenu = BIS_convMenu + [["Where's the exact source of the transmission?", _topic, "start_doe_2b", []]];
	 BIS_convMenu = BIS_convMenu + [["How do we get there?", _topic, "start_doe_2c", []]];
	 BIS_convMenu = BIS_convMenu + [["Any hostile activity?", _topic, "start_doe_2d", []]];
	 BIS_convMenu = BIS_convMenu + [["I've got everything i need. Let's go.", _topic, "start_doe_3", []]];
};

case "start_faustin_5d": {
	 BIS_convMenu = BIS_convMenu + [["What's the content of the message?", _topic, "start_doe_2a", []]];
	 BIS_convMenu = BIS_convMenu + [["Where's the exact source of the transmission?", _topic, "start_doe_2b", []]];
	 BIS_convMenu = BIS_convMenu + [["How do we get there?", _topic, "start_doe_2c", []]];
	 BIS_convMenu = BIS_convMenu + [["Any hostile activity?", _topic, "start_doe_2d", []]];
	 BIS_convMenu = BIS_convMenu + [["I've got everything i need. Let's go.", _topic, "start_doe_3", []]];
}
};

BIS_convMenu

"start.bikb"

class Sentences
{
// Doe's Sentences
class start_doe_0a
{
	text = "";
	speech[] = {"\voice\dummy.ogg"};
	class Arguments {};	
};

class start_doe_0b
{
	text = "Shut up, Dimitri!";
	speech[] = {"\voice\dummy.ogg"};
	class Arguments {};	
};

class start_doe_1a
{
	text = "So we shall check it out?";
	speech[] = {"\voice\dummy.ogg"};
	class Arguments {};	
};

class start_doe_1b
{
	text = "Fuck off, Dimitri!";
	speech[] = {"\voice\dummy.ogg"};
	class Arguments {};	
};

class start_doe_2a
{
	text = "What's the content of the radio chatter?";
	speech[] = {"\voice\dummy.ogg"};
	class Arguments {};	
};

class start_doe_2b
{
	text = "Where's the exact source of the transmission?";
	speech[] = {"\voice\dummy.ogg"};
	class Arguments {};	
};

class start_doe_2c
{
	text = "How do we get there?";
	speech[] = {"\voice\dummy.ogg"};
	class Arguments {};	
};

class start_doe_2ca
{
	text = "I'm sorry for your loss.";
	speech[] = {"\voice\dummy.ogg"};
	class Arguments {};	
};

class start_doe_2cb
{
	text = "Hey, it wasn't my fault asshole!";
	speech[] = {"\voice\dummy.ogg"};
	class Arguments {};	
};

class start_doe_2d
{
	text = "Any hostile activity?";
	speech[] = {"\voice\dummy.ogg"};
	class Arguments {};	
};

class start_doe_3
{
	text = "I've got everything i need. Let's go.";
	speech[] = {"\voice\dummy.ogg"};
	class Arguments {};	
};



// Faustin's Sentences
class start_faustin_1
{
	text = "OK, then let's start. We're picking up some strange radio transmissions from west of here.";
	speech[] = {"\voice\dummy.ogg"};
	class Arguments {};	
};

class start_faustin_2
{
	text = "We weren't able to understand anything but we've located the signal's origin. Dimitri.";
	speech[] = {"\voice\dummy.ogg"};
	class Arguments {};	
};

class start_faustin_3a
{
	text = "Yes, we don't have enough men here, so i send you. Plus you have the chance to establish contact to your forces.";
	speech[] = {"\voice\dummy.ogg"};
	class Arguments {};	
};

class start_faustin_3b
{
	text = "Shut up! Both of you! Doe, you'll go there and check out the source of the transmission.";
	speech[] = {"\voice\dummy.ogg"};
	class Arguments {};	
};

class start_faustin_4
{
	text = "We'll send you and your team out now. Any questions?";
	speech[] = {"\voice\dummy.ogg"};
	class Arguments {};	
};

class start_faustin_5a
{
	text = "We can understand only a few words. Sounds English. But our technicians are working on it.";
	speech[] = {"\voice\dummy.ogg"};
	class Arguments {};	
};

class start_faustin_5b
{
	text = "Somewhere near a village called 'Rogovo'. We can't locate it more excatly due to strong radiation.";
	speech[] = {"\voice\dummy.ogg"};
	class Arguments {};	
};

class start_faustin_5ca
{
	text = "Ladislaus is waiting for you in a truck. He has a team with him to back you up.";
	speech[] = {"\voice\dummy.ogg"};
	class Arguments {};	
};

class start_faustin_5cb
{
	text = "Hey, calm down! Any further questions?";
	speech[] = {"\voice\dummy.ogg"};
	class Arguments {};	
};

class start_faustin_5d
{
	text = "We have no information about that but i guess you'll face a few Reapers.";
	speech[] = {"\voice\dummy.ogg"};
	class Arguments {};	
};

class start_faustin_6
{
	text = "Good, the truck is waiting for you.";
	speech[] = {"\voice\dummy.ogg"};
	class Arguments {};	
};



// Rascalov's Sentences
class start_rascalov_1
{
	text = "Finally, everyone's here. Even you, Doe. The greatest warrior on the planet.";
	speech[] = {"\voice\dummy.ogg"};
	class Arguments {};	
};

class start_rascalov_2
{
	text = "It comes from far west of here. It'd take a few hours to drive there but i'm sure 'Captain America' here will get there in a second!";
	speech[] = {"\voice\dummy.ogg"};
	class Arguments {};	
};

class start_rascalov_3
{
	text = "Oh, is the little American upset? Or is he just homesick?";
	speech[] = {"\voice\dummy.ogg"};
	class Arguments {};	
};

class start_rascalov_4
{
	text = "You will go there by truck. We would send Ladislaus to back you up if he weren't dead!";
	speech[] = {"\voice\dummy.ogg"};
	class Arguments {};	
};

class start_rascalov_5a
{
	text = "Stick your pity up your ass!";
	speech[] = {"\voice\dummy.ogg"};
	class Arguments {};	
};

class start_rascalov_5b
{
	text = "Shut up or i'll kill you with my bare hands, cocksucker!";
	speech[] = {"\voice\dummy.ogg"};
	class Arguments {};	
};

class start_rascalov_6
{
	text = "Good luck, dickhead.";
	speech[] = {"\voice\dummy.ogg"};
	class Arguments {};	
};



// Interrupted
class Interrupted
{
	text = "";
	speech[] = {""};
	class Arguments {};	
};
};
class Arguments{};
class Special{};
startWithVocal[] = {hour};
startWithConsonant[] = {europe, university};

And here are two pictures of the FSMs:

unbenannt2ct.jpg

unbenanntko.jpg

And here's a link to the WIP mission if you wanna take a look: http://www.filefront.com/15247029/GS_03_Reunion.Chernarus.rar

I'd really appriciate some help!

Edit: The mission requires ACE2!

Edited by IndeedPete

Share this post


Link to post
Share on other sites

Bump. If anyone with a bit of knowlegde about FSMs would take a look at my problem please. I still have no clue where the mistake is and i'd really, really appreciate some help!

Share this post


Link to post
Share on other sites

Ha, i got it! Somehow, one of the guys didn't receive some sentences, so he couldn't answer... :rolleyes:

Share this post


Link to post
Share on other sites

Don't forget that you can't use either sleep or waitUntil in the FSMs as they're pre-compiled. If you need to add a delay into the code, you have to start a new script scope via spawn.

---------- Post added at 12:16 PM ---------- Previous post was at 11:51 AM ----------

First post updated with main advantages and manual conversation control.

Share this post


Link to post
Share on other sites

Great! Thanks Jezuro. I didn't make any voiceacting yet so i just estimate the needed time and have to use sleep.

Share this post


Link to post
Share on other sites
Example mission

And finally, here it is. The example mission contains everything that has been explained here and shows you mainly how to manage the files. It contains voice samples from the Harvest Red campaign, please don't be surprised that the conversation is a bit out of context :)

Get it here: http://www.filefront.com/15184105/convsExample.utes.zip

File is Unavailable.

Please, give worker an example.

Share this post


Link to post
Share on other sites

I tried to get it work on a simple test mission without sound files.

I have a soldier s1 and put in his init:

s1 kbAddTopic ["talk", talk.bikb]

talk.bikb:

class Sentences
{

class example_01
{
	text = "Hello Bret.";
	speech[] = {""};
	class Arguments {};

};

class example_02
{
	text = "Oh, hello Jemaine.";
	speech[] = {""};
	class Arguments {};

}
};
class Arguments{};
class Special{};
startWithVocal[] = {hour};
startWithConsonant[] = {europe, university}

But it dont works, ArmA2 just crash. Can someone help ?

Share this post


Link to post
Share on other sites

I would try to add ";" in the end of line "startWithConsonant[] = {europe, university}", just in case of mistake in the example.

Edited by Mocking-bird

Share this post


Link to post
Share on other sites

Wiggum, try removing the empty quotes from speech[] = {""};

BTW I re-uploaded the example mission to hotfile, good ol' filefront deleted it some time ago (thanks Mocking-bird for letting me know).

Edited by W0lle
Link to example mission updated

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now

×