Difference between revisions of "Conversations"

From Bohemia Interactive Community
Jump to navigation Jump to search
(Add Arma 3 note about .lip files bug, add FSM description, headers fix)
m (Some wiki formatting)
(31 intermediate revisions by 2 users not shown)
Line 1: Line 1:
{{GVI|arma2|1.00}}
+
{{TOC|side}}
 +
This documentation applies to the following games: {{GVI|arma2|1.00}} {{GVI|arma2oa|1.50}} {{GVI|TKOH|1.00}} {{GVI|arma3|0.50}}
  
  
 
== Introduction ==
 
== Introduction ==
[[Conversations]] are a system introduced with '''Arma 2''' and created to enhance discussions.
+
 
 +
[[Conversations]] is a system created to manage discussions and has been introduced in '''Arma 2'''.
 
This system allows dynamic conversations and a more fluid sentence/answer flow.
 
This system allows dynamic conversations and a more fluid sentence/answer flow.
  
Line 12: Line 14:
 
* Transparent syntax (FSM branches allow fast and efficient edits)
 
* Transparent syntax (FSM branches allow fast and efficient edits)
  
{{Important|
 
In '''Arma 3''', using a .lip file for a sound file (.ogg or .wss) creates a glitch making the sound being said about 20× faster. Normal conversations can't be made with [[say]] properly anymore, where this new conversation system will automatically use [[setRandomLip]] during the speech, avoiding the use of a corresponding .lip file.}}
 
  
 +
== Usage ==
  
== Requirements ==
+
* A '''topic''' is defined by a [[String]] for id and sentences defined in a '''Conversation .bikb''' file
'''.bikb''' extension stands for "'''B'''ohemia '''I'''nteractive '''K'''nowledge '''B'''ase" as it was originally used only for storing an AI unit's memory of what it has seen.<br>
+
* A '''[[#Conversation .bikb|Conversation .bikb]]''' lists all available sentences in a '''topic'''
This file declares the available sentences in the topic, their sound samples and the corresponding subtitles (see [[#.bikb simple example|example 1]] and [[#.bikb advanced example|example 2]]).
+
* A '''topic''' must be added to the concerned units, with eventually a '''Conversation FSM''' and/or a '''Conversation Event Handler'''
{{Important|
+
* A '''[[#Conversation FSM|Conversation FSM]]''' defines an '''AI unit''' reaction to a sentence
Sounds '''don't''' need to be defined in [[Description.ext#CfgSounds|CfgSounds]] or [[Description.ext#CfgRadio|CfgRadio]] unless you want to use them with e.g [[playSound]], [[say]] or [[groupRadio]].}}
+
* A '''[[#Conversation Event Handler|Conversation Event Handler]]''' defines a '''[[player]]'s unit''' reaction to a sentence, potentially defining available answers
 +
* If the unit is [[switchableUnits|playable]] (can be either player or AI), both should be present to ensure a proper reaction in every encountered scenario.
  
  
== Usage ==
+
== Commands ==
* a [[#Conversation FSM|Conversation FSM]] will only fire when an AI "receives" a sentence, and react accordingly.
 
* a [[#Conversation Event Handler|Conversation Event Handler]] fire when the player "receives" a sentence, and will also constantly fire ([[onEachFrame]]) as long as the player is pointing at somebody at discussion range.
 
* in the case of a playable unit (can be a player or an AI) both the FSM and the event handler must then be used.
 
  
 +
A conversation topic:
 +
# must be added to units (''via'' [[kbAddTopic]])
 +
# for the units to be able use it (''via'' [[kbTell]] for example)
 +
# once the unit said wanted sentences (checked ''via'' [[kbWasSaid]]),
 +
# the topic can be removed (''via'' [[kbRemoveTopic]]).
  
== Commands ==
+
* Related commands are listed in [[:Category:Command Group: Conversations|Conversations Command Group]]
A conversation topic has to be added (''via'' [[kbAddTopic]]) to units for them to be able use it (''via'' [[kbTell]] for example). Once the unit said wanted sentences (''via'' [[kbWasSaid]]), the topic can be removed (''via'' [[kbRemoveTopic]]).
+
* Related functions are listed in [[:Category:Function Group: Conversations|Conversations Function Group]]
  
  
 
=== Manual conversation example ===
 
=== Manual conversation example ===
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.";
 
{{codecomment|// you could - eventually - use [[kbRemoveTopic]] to "clear" the units if the topic is not required anymore. This is not mandatory.}}
 
  
 +
<sqf>
 +
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]; };
  
== Conversation .bikb file ==
+
miles kbTell [shaftoe, "briefing", "shaftoe_briefing_M_1"];
 +
waitUntil { miles kbWasSaid [shaftoe, "briefing", "shaftoe_briefing_M_1", 3]; };
 +
 
 +
hint "Conversation ended.";
 +
// kbRemoveTopic can be used here to "clear" the units if the topic is not required anymore. This is not mandatory.
 +
</sqf>
 +
 
 +
 
 +
== Conversation .bikb ==
 +
 
 +
'''.bikb''' extension stands for "'''B'''ohemia '''I'''nteractive '''K'''nowledge '''B'''ase" as it was originally used only for storing an AI unit's memory of what it has seen.<br>
 +
This file declares the available sentences in the topic, their sound samples and the corresponding subtitles (see [[#.bikb simple example|example 1]] and [[#.bikb advanced example|example 2]]).<br>
 +
As the syntax is the same as config files ([[Description.ext]], [[Config.cpp]]) [[PreProcessor Commands]] can be used.
 +
 
 +
Conversation sounds do '''not''' need to be defined in [[Description.ext#CfgSounds|CfgSounds]] or [[Description.ext#CfgRadio|CfgRadio]], unless of course they are to be used with e.g [[playSound]], [[say]] or [[groupRadio]] too.<br>
 +
As opposed to these commands, this new conversation system will automatically use [[setRandomLip]] during the speech if no .lip file is present. A .lip file can however still be used for a more precise animation.
 +
{{Feature|important|
 +
A duplicate sentence in .bikb file '''WILL''' crash the game!}}
  
  
 
=== "Interrupted" event ===
 
=== "Interrupted" event ===
The left-side conversation menu (with the list of possible sentences) can be closed via backspace at all times. If you want to handle this event as well, you have to add new sentence class called '''Interrupted''' into your .bikb file. It can then be used as a standard _sentenceId in the script.
+
 
 +
The left-side conversation menu (with the list of possible sentences) can be closed via backspace at all times. In order to handle this event as well, add a sentence class called '''Interrupted''' into the .bikb file. It can then be used as a standard {{hl|_sentenceId}} in the script.
  
  
 
=== .bikb simple example ===
 
=== .bikb simple example ===
 +
 
<syntaxhighlight lang="cpp">
 
<syntaxhighlight lang="cpp">
 
class Sentences
 
class Sentences
 
{
 
{
class example_01
+
class hello1
 +
{
 +
text = "Hello!"; // sentence subtitle - can be "text" or $STR_ stringtable reference
 +
speech[] = { "\Sound\hello1.ogg" }; // sound file path relative to mission or campaign directory
 +
class Arguments {}; // unused here
 +
};
 +
class hi_how_are_you
 
{
 
{
text = "Hello Bret."; // sentence subtitle
+
text = "Hi, how are you?";
speech[] = { "\Sound\jemaine01.ogg" }; // sound file path
+
speech[] = { "\Sound\hi_how_are_you.ogg" };
 
class Arguments {};
 
class Arguments {};
 
};
 
};
class example_02
+
class good_you
 
{
 
{
text = "Oh, hello Jemaine.";
+
text = $STR_GoodYou; // has to start with "STR_"!
speech[] = { "\Sound\bret01.ogg" };
+
speech[] = { "\Sound\good_you.ogg" };
 
class Arguments {};
 
class Arguments {};
 
};
 
};
class lastWord
+
class fine_thanks
 
{
 
{
text = $STR_TranslationReference; // has to start with "STR_"!
+
text = $STR_FineThanks;
speech[] = { "\Sound\bret01.ogg" };
+
speech[] = { "\Sound\fine_thanks" }; // no extension means .wss to the engine
 
class Arguments {};
 
class Arguments {};
 
};
 
};
  
// the Interrupted sentence is triggered when the discussion menu gets closed without answering (e.g using backspace)
+
// these sentences are here to be a full example for later Conversation Event Handler example
 +
class what_do_we_do { text = "What do we do today?"; speech[] = { "\Sound\wdwd.ogg" }; class Arguments {}; };
 +
class choose_football { text = "Let's football!"; speech[] = { "\Sound\cf.ogg" }; class Arguments {}; };
 +
class choose_bike { text = "Let's bike?"; speech[] = { "\Sound\cb.ogg" }; class Arguments {}; };
 +
class choose_arma2 { text = "I know! Arma 2"; speech[] = { "\Sound\ca.ogg" }; class Arguments {}; };
 +
 
 +
// the Interrupted sentence triggered when the conversation menu is closed without answering (e.g using backspace)
 
class Interrupted
 
class Interrupted
 
{
 
{
text = "Hey, answer me!";
+
text = "Hey, come back!";
 
speech[] = { "\Sound\interrupted.ogg" };
 
speech[] = { "\Sound\interrupted.ogg" };
 
class Arguments {};
 
class Arguments {};
Line 86: Line 116:
 
};
 
};
  
// Don't worry about these.
+
// Needed parameters.
 
class Arguments {};
 
class Arguments {};
 
class Special {};
 
class Special {};
Line 95: Line 125:
  
 
=== .bikb advanced example ===
 
=== .bikb advanced example ===
 +
 
See [[Arma_3_Radio_Protocol#Words|Words configuration]] and below for more details.
 
See [[Arma_3_Radio_Protocol#Words|Words configuration]] and below for more details.
 
<syntaxhighlight lang="cpp">
 
<syntaxhighlight lang="cpp">
Line 101: Line 132:
 
class AirstrikeRequest  
 
class AirstrikeRequest  
 
{
 
{
 +
// %team and %location will be replaced by the game engine with according values
 
text = "%team requesting close air support at grid %location ";
 
text = "%team requesting close air support at grid %location ";
speech[] = { %Team, RequestingCloseAirSupportAtGrid, %Location }; // words are defined in Cfg/RadioProtocol/Words
+
 
 +
// available words are defined in config > RadioProtocol > Words
 +
speech[] = { %Team, RequestingCloseAirSupportAtGrid, %Location };
 +
 
 
class Arguments  
 
class Arguments  
 
{
 
{
class Team { type = "simple"; };
+
class Team { type = "simple"; }; // refers to %Team, first element of speech[]
class Location { type = "simple"; };
+
// "RequestingCloseAirSupportAtGrid" is part of Radio Protocol
 +
class Location { type = "simple"; }; // refers to %Location, last element of speech[]
 
};
 
};
 
};
 
};
Line 116: Line 152:
 
startWithConsonant[] = { europe, university };
 
startWithConsonant[] = { europe, university };
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
Above sentence would be used this way:
 +
<sqf>
 +
player kbTell [
 +
BIS_HQ, // to
 +
"Airstrike", // topic
 +
"AirstrikeRequest", // sentence
 +
["Team", {}, "Anvil", ["Anvil"]], // argument 1
 +
["Location", {}, "Strelka", ["Strelka"]] // argument 2
 +
];
 +
</sqf>
 +
 +
=== .bikb BIS_fnc_kbTell example ===
 +
 +
{{Feature|arma3|
 +
<syntaxhighlight lang="cpp" style="float: right">
 +
class Sentences
 +
{
 +
class Sentence1
 +
{
 +
text = ""; // must be empty
 +
textPlain = $STR_MySubTitle;
 +
/* other parameters */
 +
};
 +
};
 +
</syntaxhighlight>
 +
'''In an addon/mod scope''', an additional subtitle entry is available to have subtitle rendered with [[BIS_fnc_showSubtitle]]: {{hl|textPlain}}, declared as shown.<br>
 +
'''This is not available for standard missions''' as [[BIS_fnc_kbTellLocal]] only checks [[configFile]] and none of [[missionConfigFile]] or [[campaignConfigFile]].
 +
<div style{{=}}"clear: both"></div>
 +
}}
 +
 +
[[BIS_fnc_kbTell]] needs a [[Description.ext]] '''CfgSentences''' entry presented as follows:
 +
<syntaxhighlight lang="cpp">
 +
class CfgSentences
 +
{
 +
class MissionName
 +
{
 +
class Event1
 +
{
 +
file = "kb\event1.bikb"; // mandatory
 +
class Sentences
 +
{
 +
class Sentence1
 +
{
 +
// the following arguments are used by BIS_fnc_kbTell (if they are defined, they are not mandatory)
 +
actor = "obi1"; // (missionNamespace getVariable actor) speaker variable name (can NOT be "player")
 +
variant = ""; // used by BIS_fnc_kbTell to define an alternative sentence
 +
variantText = ""; // used by BIS_fnc_kbTell to define an alternative sentence
 +
};
 +
class Sentence2
 +
{
 +
actor = "griev";
 +
};
 +
class Sentence3
 +
{
 +
actor = "griev";
 +
};
 +
};
 +
};
 +
};
 +
};
 +
</syntaxhighlight>
 +
 +
with a .bikb file looking like this:
 +
<syntaxhighlight lang="cpp">
 +
class Sentences
 +
{
 +
class Sentence1
 +
{
 +
text = $STR_HelloThere;
 +
speech[] = { "\Sound\hello.ogg" };
 +
class Arguments {};
 +
};
 +
class Sentence2
 +
{
 +
text = $STR_GeneralKenobi;
 +
speech[] = { "Sound\gk.ogg" };
 +
class Arguments {};
 +
};
 +
class Sentence3
 +
{
 +
text = $STR_YouAreABoldOne;
 +
speech[] = { "Sound\bold.ogg" };
 +
class Arguments {};
 +
};
 +
};
 +
class Arguments {};
 +
class Special {};
 +
startWithVocal[] = { hour };
 +
startWithConsonant[] = { europe, university };
 +
</syntaxhighlight>
 +
 +
 +
A tidier way of doing so is to put all the needed information in the '''.bikb''' file:
 +
<syntaxhighlight lang="cpp">
 +
class Sentences
 +
{
 +
class Sentence1
 +
{
 +
text = $STR_HelloThere;
 +
speech[] = { "\Sound\hello.ogg" };
 +
class Arguments {};
 +
actor = "obi1";
 +
};
 +
class Sentence2
 +
{
 +
text = $STR_GeneralKenobi;
 +
speech[] = { "Sound\gk.ogg" };
 +
class Arguments {};
 +
actor = "griev";
 +
};
 +
class Sentence3
 +
{
 +
text = $STR_YouAreABoldOne;
 +
speech[] = { "Sound\bold.ogg" };
 +
class Arguments {};
 +
actor = "griev";
 +
};
 +
};
 +
class Arguments {};
 +
class Special {};
 +
startWithVocal[] = { hour };
 +
startWithConsonant[] = { europe, university };
 +
</syntaxhighlight>
 +
 +
And to include it ''via'' {{ic|#include}} in [[Description.ext]]:
 +
<syntaxhighlight lang="cpp">
 +
class CfgSentences
 +
{
 +
class MissionName
 +
{
 +
class Event1
 +
{
 +
file = "kb\event1.bikb";
 +
#include "kb\event1.bikb" // avoids a double declaration
 +
};
 +
};
 +
};
 +
</syntaxhighlight>
 +
 +
 +
''Et voilà !'' All that remains to be done now is to call the discussion like this: {{ic|["Event1", "MissionName"] [[call]] [[BIS_fnc_kbTell]]; {{cc|note that the topic comes first}}}};
 +
the game will make the defined actors say the lines in the order they were declared. [[BIS_fnc_kbTell]] will manage to use [[kbAddTopic]], [[kbTell]] and other commands.
 +
 +
 +
{{Feature|important|Please note:
 +
* use '''class CfgSentences''' in [[Description.ext]]
 +
* use '''class Sentences''' in .bikb files}}
  
  
 
== Conversation FSM ==
 
== Conversation FSM ==
As stated earlier, the '''Conversation FSM''' fires only when '''an AI''' "receives" a sentence and defines its reaction.
+
 
 +
As stated earlier, the '''Conversation FSM''' fires on sentences '''received by an AI''' and defines an AI unit's reaction.
 
The Conversation FSM receives the following arguments:
 
The Conversation FSM receives the following arguments:
 
* '''_this:''' [[Object]] - receiver or listener of the sentence. One of the units that had this particular script assigned ''via'' [[kbAddTopic]]
 
* '''_this:''' [[Object]] - receiver or listener of the sentence. One of the units that had this particular script assigned ''via'' [[kbAddTopic]]
Line 129: Line 313:
  
 
=== Conversation FSM example ===
 
=== Conversation FSM example ===
{{note|WIP}}
+
 
 +
[[Image:ConversationFSM_example.png|thumb|left|800px|A simple FSM that AI uses to react to a speaker's sentences]]
 +
{{Clear}}
  
  
 
== Conversation Event Handler ==
 
== Conversation Event Handler ==
 +
 
The '''Conversation Event Handler''' is '''not''' an [[:Category:Event_Handlers|Event Handler]] as we know them.
 
The '''Conversation Event Handler''' is '''not''' an [[:Category:Event_Handlers|Event Handler]] as we know them.
This is a [[code]] that will be executed with each sentence '''received by a player''' as well as when the player is pointing at a "talkable" person. Following parameters are passed:
+
It is [[Code]] that will be executed with each sentence '''received by a player''', and will constantly fire ([[onEachFrame]]) when the player is pointing at a "talkable" person at "talkable" range. Following parameters are passed:
 
* '''_this:''' [[Object]] - the [[player]] receiving the sentence. Must have had this particular script assigned ''via'' [[kbAddTopic]]
 
* '''_this:''' [[Object]] - the [[player]] receiving the sentence. Must have had this particular script assigned ''via'' [[kbAddTopic]]
 
* '''_from:''' [[Object]] - the unit that told the sentence
 
* '''_from:''' [[Object]] - the unit that told the sentence
* '''_sentenceId:''' [[String]] - the sentence this unit is reacting to. Defined in .bikb in class Sentences
+
* '''_sentenceId:''' [[String]] - the sentence the player is reacting to. Defined in .bikb in class Sentences
 
* '''_topic:''' [[String]] - topic name used in [[kbAddTopic]]
 
* '''_topic:''' [[String]] - topic name used in [[kbAddTopic]]
 +
 +
Its return format is an [[Array]] of arrays (or an empty array) and can look like this:
 +
<sqf>
 +
[
 +
[
 +
localize "STR_AnswerTitle1", // conversation menu's display text
 +
"myTopic", // topic to answer to
 +
"answer1SentenceId", // sentence ID
 +
[] // sentence arguments
 +
],
 +
[
 +
localize "STR_AnswerTitle2",
 +
"myTopic",
 +
"answer2SentenceId",
 +
[]
 +
]
 +
];
 +
</sqf>
 +
 +
There is a certain resemblance with the [[kbTell]] syntax; note that the ''forceRadio'' parameter is not present.
 +
 +
{{Feature|important|The Conversation Event Handler's code is executed in [[Scheduler#Unscheduled_Environment|'''unscheduled''' environment]] !}}
  
  
 
=== Conversation Event Handler example ===
 
=== Conversation Event Handler example ===
{{codecomment|// here we will be storing all the sentences from which the player will choose (the menu on the left side of the screen)
+
 
// if there is only one option in the array, the sentence will replace the "Talk to" action name}}
+
<sqf>
+
// here we will be storing all the sentences from which the player will choose (left side menu)
  BIS_convMenu = [];
+
// if there is only one option in the array, the sentence will replace the "Talk to" action name
+
 
{{codecomment|// we want the player to be able to approach his buddy and talk to him via the action menu.
+
private _convMenu = [];
// we need to check:
+
 
// if the player is pointing at his buddy
+
// we want the player to be able to approach his buddy and talk to him via the action menu.
// if the player is not answering any of his buddy's sentences
+
// we need to check:
// if the player hasn't told him hello already
+
// if the player is pointing at his buddy
// then we add that array to BIS_convMenu - the parameters are mostly self-explanatory:}}
+
// if the player is not answering any of his buddy's sentences
+
// if the player has not told him hello already
[[if]] (_from == buddy1 && _sentenceId == "" && !(_this [[kbWasSaid]] [_from, _topic, "hello1", 999999])) then
+
// then we add that array to _convMenu - the parameters are mostly self-explanatory:
{
+
 
BIS_convMenu = BIS_convMenu + [["Say hello.", _topic, "hello1", []]];
+
if (_from == buddy1 && _sentenceId == "" && !(_this kbWasSaid [_from, _topic, "hello1", 999999])) then
};
+
{
+
_convMenu pushBack ["Say hello.", _topic, "hello1", []];
{{codecomment|// here we make the unit say the proper sentence based on the one he just received
+
};
// [[switch]]-[[case]]-[[do]] is used here but it is completely up to you how to evaluate it ([[if]]-[[then]] etc.)}}
+
 
[[switch]] (_sentenceId) [[do]]
+
// here we make the unit say the proper sentence based on the one he just received
{
+
// switch-case-do is used here but it is only one solution to manage sentences (if-then etc could do)
[[case]] "hello1":
+
 
{
+
switch (_sentenceId) do
_this [[kbTell]] [_from, _topic, "hi_how_are_you"];
+
{
};
+
case "hello1":
[[case]] "good_you":
+
{
{
+
_this kbTell [_from, _topic, "hi_how_are_you"];
_this [[kbTell]] [_from, _topic, "fine_thanks"];
+
};
};
+
case "good_you":
[[case]] "what_do_we_do_today": {
+
{
{{codecomment|// the player will have 3 answers to choose from:}}
+
_this kbTell [_from, _topic, "fine_thanks"];
BIS_convMenu = BIS_convMenu + [["Football.", _topic, "choose_football", []]];
+
};
BIS_convMenu = BIS_convMenu + [["Bike.", _topic, "choose_bike", []]];
+
case "what_do_we_do":
BIS_convMenu = BIS_convMenu + [["Arma II.", _topic, "choose_arma2", []]]
+
{
};
+
// the player will have 3 answers to choose from:
};
+
_convMenu pushBack ["Football.", _topic, "choose_football", []];
+
_convMenu pushBack ["Bike.", _topic, "choose_bike", []];
{{codecomment|// return the sentence list pool}}
+
_convMenu pushBack ["Armed AssaultI.", _topic, "choose_arma2", []];
BIS_convMenu;
+
};
 +
};
 +
 
 +
// return the sentence list pool
 +
_convMenu;
 +
</sqf>
  
  
 
== See also ==
 
== See also ==
 +
 
* [[:Category:Command Group: Conversations|Conversations Command Group]]
 
* [[:Category:Command Group: Conversations|Conversations Command Group]]
 
* [[:Category:Function Group: Conversations|Conversations Function Group]]
 
* [[:Category:Function Group: Conversations|Conversations Function Group]]
* [https://forums.bohemia.net/forums/topic/89071-new-conversation-system-how-to/ Jezuro's introduction forum post]
+
* {{ExternalLink|link= https://forums.bohemia.net/forums/topic/89071-new-conversation-system-how-to/|text= Jezuro's introduction forum post}}
* [https://forums.bohemia.net/forums/topic/107662-using-dialogue-system-between-player-and-ai/ HateDread's forum post and Evil_Echo's answers]
+
* {{ExternalLink|link= https://forums.bohemia.net/forums/topic/107662-using-dialogue-system-between-player-and-ai/|text= HateDread's forum post and Evil_Echo's answers}}
* [[FSM]], [[FSM Editor]]
+
* [[FSM]]
 +
* [[FSM Editor]]
 +
 
 +
 
 +
[[Category:Scripting Topics]]
 +
[[Category:Introduced with Arma 2 version 1.00]]

Revision as of 01:22, 25 June 2022

This documentation applies to the following games: Logo A2.png1.00 A2 OA Logo.png1.50 tkoh logo small.png1.00 Arma 3 logo black.png0.50


Introduction

Conversations is a system created to manage discussions and has been introduced in Arma 2. This system allows dynamic conversations and a more fluid sentence/answer flow.

Its main advantages are:

  • 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)


Usage

  • A topic is defined by a String for id and sentences defined in a Conversation .bikb file
  • A Conversation .bikb lists all available sentences in a topic
  • A topic must be added to the concerned units, with eventually a Conversation FSM and/or a Conversation Event Handler
  • A Conversation FSM defines an AI unit reaction to a sentence
  • A Conversation Event Handler defines a player's unit reaction to a sentence, potentially defining available answers
  • If the unit is playable (can be either player or AI), both should be present to ensure a proper reaction in every encountered scenario.


Commands

A conversation topic:

  1. must be added to units (via kbAddTopic)
  2. for the units to be able use it (via kbTell for example)
  3. once the unit said wanted sentences (checked via kbWasSaid),
  4. the topic can be removed (via kbRemoveTopic).


Manual conversation example

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."; // kbRemoveTopic can be used here to "clear" the units if the topic is not required anymore. This is not mandatory.


Conversation .bikb

.bikb extension stands for "Bohemia Interactive Knowledge Base" as it was originally used only for storing an AI unit's memory of what it has seen.
This file declares the available sentences in the topic, their sound samples and the corresponding subtitles (see example 1 and example 2).
As the syntax is the same as config files (Description.ext, Config.cpp) PreProcessor Commands can be used.

Conversation sounds do not need to be defined in CfgSounds or CfgRadio, unless of course they are to be used with e.g playSound, say or groupRadio too.
As opposed to these commands, this new conversation system will automatically use setRandomLip during the speech if no .lip file is present. A .lip file can however still be used for a more precise animation.

A duplicate sentence in .bikb file WILL crash the game!


"Interrupted" event

The left-side conversation menu (with the list of possible sentences) can be closed via backspace at all times. In order to handle this event as well, add a sentence class called Interrupted into the .bikb file. It can then be used as a standard _sentenceId in the script.


.bikb simple example

class Sentences
{
	class hello1
	{
		text = "Hello!";					// sentence subtitle - can be "text" or $STR_ stringtable reference
		speech[] = { "\Sound\hello1.ogg" };	// sound file path relative to mission or campaign directory
		class Arguments {};					// unused here
	};
	class hi_how_are_you
	{
		text = "Hi, how are you?";
		speech[] = { "\Sound\hi_how_are_you.ogg" };
		class Arguments {};
	};
	class good_you
	{
		text = $STR_GoodYou; // has to start with "STR_"!
		speech[] = { "\Sound\good_you.ogg" };
		class Arguments {};
	};
	class fine_thanks
	{
		text = $STR_FineThanks;
		speech[] = { "\Sound\fine_thanks" }; // no extension means .wss to the engine
		class Arguments {};
	};

	// these sentences are here to be a full example for later Conversation Event Handler example
	class what_do_we_do	{ text = "What do we do today?"; speech[] = { "\Sound\wdwd.ogg" };	class Arguments {}; };
	class choose_football	{ text = "Let's football!";		 speech[] = { "\Sound\cf.ogg" };	class Arguments {}; };
	class choose_bike		{ text = "Let's bike?";			 speech[] = { "\Sound\cb.ogg" };	class Arguments {}; };
	class choose_arma2		{ text = "I know! Arma 2";		 speech[] = { "\Sound\ca.ogg" };	class Arguments {}; };

	// the Interrupted sentence triggered when the conversation menu is closed without answering (e.g using backspace)
	class Interrupted
	{
		text = "Hey, come back!";
		speech[] = { "\Sound\interrupted.ogg" };
		class Arguments {};
	};
};

// Needed parameters.
class Arguments {};
class Special {};
startWithVocal[] = { hour };
startWithConsonant[] = { europe, university };


.bikb advanced example

See Words configuration and below for more details.

class Sentences
{
	class AirstrikeRequest 
	{
		// %team and %location will be replaced by the game engine with according values
		text = "%team requesting close air support at grid %location ";

		// available words are defined in config > RadioProtocol > Words
		speech[] = { %Team, RequestingCloseAirSupportAtGrid, %Location };

		class Arguments 
		{
			class Team { type = "simple"; };		// refers to %Team, first element of speech[]
			// "RequestingCloseAirSupportAtGrid" is part of Radio Protocol
			class Location { type = "simple"; };	// refers to %Location, last element of speech[]
		};
	};
};

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

Above sentence would be used this way:

player kbTell [ BIS_HQ, // to "Airstrike", // topic "AirstrikeRequest", // sentence ["Team", {}, "Anvil", ["Anvil"]], // argument 1 ["Location", {}, "Strelka", ["Strelka"]] // argument 2 ];

.bikb BIS_fnc_kbTell example

Arma 3
class Sentences
{
	class Sentence1
	{
		text = ""; // must be empty
		textPlain = $STR_MySubTitle;
		/* other parameters */
	};
};

In an addon/mod scope, an additional subtitle entry is available to have subtitle rendered with BIS_fnc_showSubtitle: textPlain, declared as shown.
This is not available for standard missions as BIS_fnc_kbTellLocal only checks configFile and none of missionConfigFile or campaignConfigFile.

BIS_fnc_kbTell needs a Description.ext CfgSentences entry presented as follows:

class CfgSentences
{
	class MissionName
	{
		class Event1
		{
			file = "kb\event1.bikb";	// mandatory
			class Sentences
			{
				class Sentence1
				{
					// the following arguments are used by BIS_fnc_kbTell (if they are defined, they are not mandatory)
					actor = "obi1";		// (missionNamespace getVariable actor) speaker variable name (can NOT be "player")
					variant = "";		// used by BIS_fnc_kbTell to define an alternative sentence
					variantText = "";	// used by BIS_fnc_kbTell to define an alternative sentence
				};
				class Sentence2
				{
					actor = "griev";
				};
				class Sentence3
				{
					actor = "griev";
				};
			};
		};
	};
};

with a .bikb file looking like this:

class Sentences
{
	class Sentence1
	{
		text = $STR_HelloThere;
		speech[] = { "\Sound\hello.ogg" };
		class Arguments {};
	};
	class Sentence2
	{
		text = $STR_GeneralKenobi;
		speech[] = { "Sound\gk.ogg" };
		class Arguments {};
	};
	class Sentence3
	{
		text = $STR_YouAreABoldOne;
		speech[] = { "Sound\bold.ogg" };
		class Arguments {};
	};
};
class Arguments {};
class Special {};
startWithVocal[] = { hour };
startWithConsonant[] = { europe, university };


A tidier way of doing so is to put all the needed information in the .bikb file:

class Sentences
{
	class Sentence1
	{
		text = $STR_HelloThere;
		speech[] = { "\Sound\hello.ogg" };
		class Arguments {};
		actor = "obi1";
	};
	class Sentence2
	{
		text = $STR_GeneralKenobi;
		speech[] = { "Sound\gk.ogg" };
		class Arguments {};
		actor = "griev";
	};
	class Sentence3
	{
		text = $STR_YouAreABoldOne;
		speech[] = { "Sound\bold.ogg" };
		class Arguments {};
		actor = "griev";
	};
};
class Arguments {};
class Special {};
startWithVocal[] = { hour };
startWithConsonant[] = { europe, university };

And to include it via #include in Description.ext:

class CfgSentences
{
	class MissionName
	{
		class Event1
		{
			file = "kb\event1.bikb";
			#include "kb\event1.bikb" // avoids a double declaration
		};
	};
};


Et voilà ! All that remains to be done now is to call the discussion like this: ["Event1", "MissionName"] call BIS_fnc_kbTell; // note that the topic comes first; the game will make the defined actors say the lines in the order they were declared. BIS_fnc_kbTell will manage to use kbAddTopic, kbTell and other commands.


Please note:
  • use class CfgSentences in Description.ext
  • use class Sentences in .bikb files


Conversation FSM

As stated earlier, the Conversation FSM fires on sentences received by an AI and defines an AI unit's reaction. The Conversation FSM receives the following arguments:

  • _this: Object - receiver or listener of the sentence. One of the units that had this particular script assigned via kbAddTopic
  • _from: Object - the unit that told the sentence
  • _sentenceId: String - the sentence this unit is reacting to. Defined in .bikb in class Sentences
  • _topic: String - topic name used in kbAddTopic

See also: FSM, FSM Editor


Conversation FSM example

A simple FSM that AI uses to react to a speaker's sentences


Conversation Event Handler

The Conversation Event Handler is not an Event Handler as we know them. It is Code that will be executed with each sentence received by a player, and will constantly fire (onEachFrame) when the player is pointing at a "talkable" person at "talkable" range. Following parameters are passed:

  • _this: Object - the player receiving the sentence. Must have had this particular script assigned via kbAddTopic
  • _from: Object - the unit that told the sentence
  • _sentenceId: String - the sentence the player is reacting to. Defined in .bikb in class Sentences
  • _topic: String - topic name used in kbAddTopic

Its return format is an Array of arrays (or an empty array) and can look like this:

[ [ localize "STR_AnswerTitle1", // conversation menu's display text "myTopic", // topic to answer to "answer1SentenceId", // sentence ID [] // sentence arguments ], [ localize "STR_AnswerTitle2", "myTopic", "answer2SentenceId", [] ] ];

There is a certain resemblance with the kbTell syntax; note that the forceRadio parameter is not present.

The Conversation Event Handler's code is executed in unscheduled environment !


Conversation Event Handler example

// here we will be storing all the sentences from which the player will choose (left side menu) // if there is only one option in the array, the sentence will replace the "Talk to" action name private _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 the player is pointing at his buddy // if the player is not answering any of his buddy's sentences // if the player has not told him hello already // then we add that array to _convMenu - the parameters are mostly self-explanatory: if (_from == buddy1 && _sentenceId == "" && !(_this kbWasSaid [_from, _topic, "hello1", 999999])) then { _convMenu pushBack ["Say hello.", _topic, "hello1", []]; }; // here we make the unit say the proper sentence based on the one he just received // switch-case-do is used here but it is only one solution to manage sentences (if-then etc could do) 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": { // the player will have 3 answers to choose from: _convMenu pushBack ["Football.", _topic, "choose_football", []]; _convMenu pushBack ["Bike.", _topic, "choose_bike", []]; _convMenu pushBack ["Armed AssaultI.", _topic, "choose_arma2", []]; }; }; // return the sentence list pool _convMenu;


See also