Jump to content
IndeedPete

Spin-Off Release: Simple Conversation System

Recommended Posts

Well, it is already better, than was!

After small changes i did it:)

I excluded parameter _len and replaced sleep _len with sleep _add in simpleSentence.sqf.

I have three phrases with their sounds (5,4 and 6 sec. length everyone)

[

[iP_Commander, player],

[

["Very glad to hear you",5],

["Hey, you promise me about your case",4],

["Yeah, Nikos had to tell you",6]

],

"DIRECT",

true

] spawn IP_fnc_simpleConversation;

playsound "SDAYS_021";

sleep 5;

playsound "SDAYS_022";

sleep 4;

playsound "SDAYS_023";

sleep 6;

It is more evident for me, and it works well.

About kbtell: when leaving empty a text field (text = ""; speech[] = {"\sounds\SDAYS_001.ogg"}), duplication won't be (text inside kbtell's sidechat won't be shown)

IndeedPete, thanks a lot for help!

Do you plan to post a similar script for "take money from deads" (as it is realized in your campaign MERCS)?

Edited by Anrio

Share this post


Link to post
Share on other sites

Nice to hear it's working!

No, it's not planned. A money system is quite easy to implement but it doesn't really make sense without a shop where you can spend the money. The sole money system just consists of three functions + some notifications though. Posted them below, just for the interested folks.

Init:

player setVariable ["IP_Money", 0];
player createDiarySubject ["Money", "Account"];
player createDiaryRecord ["Money", [(name player), "Account Balance: +" + str(player getVariable ["IP_Money", 0]) + "€"]];

IP_fnc_takeMoney (called on objects or units, adds the action post-mortem):

_addAction = {
(_this select 0) addAction ["Take Money", {
	_object = _this select 0;
	_id = _this select 2;
	_amountMin = _object getVariable ["IP_AmountMin", 0];
	_amountMax = _object getVariable ["IP_AmountMax", 50];
	_delete = _object getVariable ["IP_DeleteObject", false];

	_total = _amountMin + round(random(_amountMax - _amountMin));
	_total call IP_fnc_addMoney;	
	_object removeAction _id;
	if (_delete) then {deleteVehicle _object};
}, [], 1.5, false, true, "", "(_this distance _target < 3)"];
};

_object = [_this, 0, objNull, [objNull]] call BIS_fnc_param;
_amountMin = [_this, 1, 1, [0]] call BIS_fnc_param;
_amountMax = [_this, 2, 50, [0]] call BIS_fnc_param;

if ((_amountMin < 1) OR (_amountMax < 1)) exitWith {};

_object setVariable ["IP_AmountMin", _amountMin];
_object setVariable ["IP_AmountMax", _amountMax];

if (_object isKindOf "Man") then {
_object setVariable ["IP_DeleteObject", false];	
_object addEventhandler ["Killed", _addAction];
} else {
_object setVariable ["IP_DeleteObject", true];	
[_object] call _addAction;
};

IP_fnc_addMoney (the function that actually adds money to the player):

_add = _this;

if (_add < 1) exitWith {false};
_money = player getVariable ["IP_Money", 0];
_money = _money + _add;
player setVariable ["IP_Money", _money];
player createDiaryRecord ["Money", [(name player), "<br/>Deposit: +" + str(_add) + "€<br/>New Account Balance: +" + str(_money) + "€"]];
if (true) then {["MoneyAdded", [_add]] call bis_fnc_showNotification};
true

IP_fnc_purchase (just for the sake of completion the function that can remove money from the player):

private "_res";

_price = _this;
if (_price < 1) exitWith {false};
_money = player getVariable ["IP_Money", 0];

_newMoney = _money - _price;
if (_newMoney < 0) then {
hint format ["You don't have enough money!\nMoney needed: €%1\nIn possession: €%2", _price, _money];
_res = false;
} else {
player setVariable ["IP_Money", _newMoney];
player createDiaryRecord ["Money", [(name player), "Withdrawal: -" + str(_price) + "€<br/>New Account Balance: +" + str(_newMoney) + "€"]];
if (true) then {["MoneySpent", [_price]] call bis_fnc_showNotification};
_res = true;
};

_res

And the notifications:

class CfgNotifications
{
// Shop
class MoneyAdded
{
	title = "ADDED MONEY";
	iconPicture = "\A3\ui_f\data\map\mapcontrol\taskIcondone_ca.paa";
	description = "+%1€";
	color[] = {0.6,0.8,0.4,1};
	priority = 7;
};		
class MoneySpent
{
	title = "SPENT MONEY";
	iconPicture = "\A3\ui_f\data\map\mapcontrol\taskIconfailed_ca.paa";
	description = "-%1€";
	color[] = {1,0.1,0,1};
	priority = 7;
};
};

Share this post


Link to post
Share on other sites

Thanks for info!

I'll try to create the weapon shop such one as in your company.

Share this post


Link to post
Share on other sites

You're free to open up the M.E.R.C.S. PBO and have a look. I've successfully ported the shop to another mission already. I might be able to upload an export here later. Of course it would be totally off the record as it's not really documented and I'm not ready yet for a public release with proper explanations and support and stuff. Questions are always welcome but for now this export would probably address more advanced scripters who like to fiddle around a bit.

Share this post


Link to post
Share on other sites

Hello Indeedpete,

I am trying to have a task be given and have a marker be hidden (the task involves investigating an area). I tried the following but once this is in the classname, the task doesn’t appear at all:

class job

{

expression = "[player, ['tFindsam','tHnS'], ['Find Sam <marker name='msamarea''>Sam</marker>?', 'Find Sam.', 'Possible area], 'msamarea', setmarkersizelocal [0,0], true, 1] call BIS_fnc_taskCreate;";

responses[] = {"maxijob"};

sentences[] = {

"I'm all ears.",

". . Sam. . get to. . him."

};

};

If I take out the script in red, the task appears fine, but I can see the marker. Any way of making this marker invisible?

Started playing MERCS, got to say Im loving it! I really like the sleep action. How were you able to do that?

Share this post


Link to post
Share on other sites

You're passing stuff to the BIS_fnc_taskCreate which just doesn't belong there. If you're not already doing so, start your game with -showScriptErrors. It will pop out a hint every time an error occurs during a script.

Parameters for BIS_fnc_taskCreate can be found here.

To hide a marker I usually use setMarkerAlpha. So in your case it would be something like this:

class job
{
expression = "'msamarea' setMarkerAlpha 0; [player, ['tFindsam','tHnS'], ['Find Sam <marker name=''msamarea''>Sam</marker>?', 'Find Sam.', 'Possible area'], 'msamarea', true, 1] call BIS_fnc_taskCreate;";
responses[] = {"maxijob"};
sentences[] = {
	"I'm all ears.",
	". . Sam. . get to. . him."
};
}; 

Glad you like it! Some issues arose with the latest Arma 3 patches but it should still be playable. Mostly the stupid randomisation scripts are changing faces and stuff. :/

The sleep script is just a small dialog + skipTime, nothing special:

Dialog Source

class IP_DLG_REST
{
   idd = 30000;
   movingenable = true;   

   class Controls
   {
	class IP_BOX_MAIN: BOX
	{
		idc = -1;
		text = ""; //--- ToDo: Localize;
		x = 4 * GUI_GRID_W + GUI_GRID_X;
		y = 11 * GUI_GRID_H + GUI_GRID_Y;
		w = 32 * GUI_GRID_W;
		h = 7 * GUI_GRID_H;
	};

	////////////////////////////////////////////////////////
	// GUI EDITOR OUTPUT START (by IndeedPete, v1.063, #Bejogi)
	////////////////////////////////////////////////////////

	class IP_FRM_MAIN: RscFrame
	{
		idc = 1800;
		text = "Rest"; //--- ToDo: Localize;
		x = 4 * GUI_GRID_W + GUI_GRID_X;
		y = 11 * GUI_GRID_H + GUI_GRID_Y;
		w = 32 * GUI_GRID_W;
		h = 7 * GUI_GRID_H;
	};
	class IP_BTN_CANCEL: RscButton
	{
		idc = 1600;
		text = "Cancel"; //--- ToDo: Localize;
		x = 31 * GUI_GRID_W + GUI_GRID_X;
		y = 15.5 * GUI_GRID_H + GUI_GRID_Y;
		w = 4 * GUI_GRID_W;
		h = 2 * GUI_GRID_H;
		action = "closeDialog 0";
	};
	class IP_BTN_REST: RscButton
	{
		idc = 1601;
		text = "Rest"; //--- ToDo: Localize;
		x = 26 * GUI_GRID_W + GUI_GRID_X;
		y = 15.5 * GUI_GRID_H + GUI_GRID_Y;
		w = 4 * GUI_GRID_W;
		h = 2 * GUI_GRID_H;
		action = "nul = [(sliderPosition 1900)] spawn IP_fnc_rest";
	};
	class IP_SLD_HOURS: RscSlider
	{
		idc = 1900;
		x = 4.5 * GUI_GRID_W + GUI_GRID_X;
		y = 12 * GUI_GRID_H + GUI_GRID_Y;
		w = 30.5 * GUI_GRID_W;
		h = 2.5 * GUI_GRID_H;
		onSliderPosChanged = "[1000, (sliderPosition 1900)] call IP_fnc_setRestHoursTime";
	};
	class IP_TXT_HOURS: RscText
	{
		idc = 1000;
		text = "24 Hours - 12:00 AM"; //--- ToDo: Localize;
		x = 4.5 * GUI_GRID_W + GUI_GRID_X;
		y = 15.5 * GUI_GRID_H + GUI_GRID_Y;
		w = 14 * GUI_GRID_W;
		h = 2 * GUI_GRID_H;
	};
	////////////////////////////////////////////////////////
	// GUI EDITOR OUTPUT END
	////////////////////////////////////////////////////////
};
};

IP_fnc_openRestDialog

createDialog "IP_DLG_REST";
sliderSetRange [1900, 1, 24];
sliderSetPosition [1900, 1];
[1000, (sliderPosition 1900)] call IP_fnc_setRestHoursTime;

IP_fnc_setRestHoursTime

_idc = [_this, 0, 1000, [0]] call BIS_fnc_param;
_hours = [_this, 1, 1, [0]] call BIS_fnc_param;

_textHours = [_hours, "HH:MM"] call BIS_fnc_timeToString;
_hours = if ((daytime + _hours) > 24) then {(daytime + _hours - 24)} else {(daytime + _hours)};
_textDaytime = [_hours, "HH:MM"] call BIS_fnc_timeToString;

ctrlSetText [_idc, (_textHours + " Hour(s) - Until " + _textDaytime)];

IP_fnc_rest

_time = [_this, 0, 0, [0]] call BIS_fnc_param;

closeDialog 0;
["IP_BlackScreen", false] call BIS_fnc_blackOut;

sleep 2;
skipTime _time;
if (getText(missionConfigFile >> "name") in ["Hub01", "Hub02"]) then {
call IP_scn_dynamicCampInit;
if (_time >= 1) then {IP_Weather = [(IP_Weather select 0)] call IP_fnc_setWeather};
};

["IP_BlackScreen"] call BIS_fnc_blackIn;

sleep 1;

[] call IP_fnc_showOSD;

And to implement it just an action on a sleeping mattress.

this addAction ["Rest", {nul = [] spawn IP_fnc_openRestDialog}];

Share this post


Link to post
Share on other sites

Thanks IndeedPete,

1. I tried

'msamarea' setMarkerAlpha 0
but the little task icon still appears on the map.

2. This is the set up: inside the mission folder I have a folder named rest.

Inside the rest, I have 2 folders cfg and fnc: cfg has dialogs.hpp while fnc has the other three fnc files you provided.

The Rest action pops up when near the sleeping bag, but nothing happens when its chosen. Do I need a special marker for the sleep to work?

Share this post


Link to post
Share on other sites

1. Where do you execute this line?

2. Combine the conv and rest fnc folders and as well as the two dialogs.hpps from conv and rest. Extend your functions.hpp with the following category:

class rest
	{
		class openRestDialog
		{
			file = "Campaigns\IP_CMP_MERCS\fnc\rest\openRestDialog.sqf";
		};
		class rest
		{
			file = "Campaigns\IP_CMP_MERCS\fnc\rest\rest.sqf";
		};
		class setRestHoursTime
		{
			file = "Campaigns\IP_CMP_MERCS\fnc\rest\setRestHoursTime.sqf";
		};
	};

Share this post


Link to post
Share on other sites

Thanks for the quick response IndeedPete :D

1)Its in the execution part of the dialogue class, just like in your example.

2)The folders are combined and here's what happened. This error came out when the dialogs were combined:

/IP_DLG_REST/Controls.IP_BOX_MAIN: Undefined base class 'BOX'

After looking at the old dialog, it was changed to class IP_BOX_MAIN: IP_BOX. I did the same to

class IP_FRM_MAIN: IP_RscFrame

class IP_BTN_CANCEL: IP_RscButton

class IP_BTN_REST: IP_RscButton

class IP_SLD_HOURS: IP_RscSlider

class IP_TXT_HOURS: IP_RscText.

The errors stopped when the IP_ was added to all those classnames. It loaded up fine, the rest action appeared but nothing happens after selecting it.

In regards to the functions.hpp

heres what i got

class rest

{

class openRestDialog

{

file = "conv\fnc\openRestDialog.sqf";

};

class rest

{

file = "conv\fnc\rest.sqf";

};

class setRestHoursTime

{

file = "conv\fnc\setRestHoursTime.sqf";

};

};

class IP

{

class conv

{

class addConversation

{

file = "conv\fnc\addConversation.sqf";

};

class closeConversation

{

file = "conv\fnc\closeConversation.sqf";

};

class openConversation

{

file = "conv\fnc\openConversation.sqf";

};

class removeConversation

{

file = "conv\fnc\removeConversation.sqf";

};

class selectResponse

{

file = "conv\fnc\selectResponse.sqf";

};

class simpleConversation

{

file = "conv\fnc\simpleConversation.sqf";

};

class simpleSentence

{

file = "conv\fnc\simpleSentence.sqf";

};

};

};

Share this post


Link to post
Share on other sites

1. Then it will only disappear once the conversation took place. If not, better double check if the spelling of the marker name is correct. And if you didn't do so, it's a good idea to run the game with -showScriptErrors when you're editing stuff.

2. The class rest must be within the class IP. I'll take a closer look at your post later as I'm at work right now.^^

Share this post


Link to post
Share on other sites

1. Double checked it. No errors when the ai gives the task. Task marker still appears. Once part of the task is completed, this appears on the bottom of the srcreen: [bIS_fnc_taskCreate] #6: is type SCALAR, must be BOOL. true used instead. What is this?

2. Alright it works now. Thanks IndeedPete!

3. There's a trigger that plays a video, and skips time. Is it possible to call the trigger in the execution part of the class convo?

Share this post


Link to post
Share on other sites

1. That's the BIS function saying you've passed wrong parameters. Maybe you could provide a small repro-mission?

3. Yes, sure. Though if planning to execute longer scripts consider putting them into an extra file and call them from the custom code attribute with execVM or better compile them first and just use call.

Share this post


Link to post
Share on other sites

1. Here it is https://www.dropbox.com/s/pnua9imr8nh2dmp/dissapear%2520task%2520practice.Stratis.pbo?dl=0

3. About the calling of units. In the above example there's a choice that calls the script using execVM. I want to spawn different units at different times. The only way I know right now would be to create scripts for each group of units or use those triggers with the script inside. That's not the best way to do it right, since the mission folder will have a ton of scripts. You mentioned to compile them first and just use call. What is that?

Thanks for all your help man!:bounce3:

Share this post


Link to post
Share on other sites

1. There's at least two mistakes from what I can see. There's an ' too much in "don't". Better use "do not" to avoid mistakes or put it into a separate script. So, it should look like:

nul = [maxi, 'Take this letter to one of my associates. [b][color="#800000"]Do not[/color][/b] mess this up.', 'DIRECT', 1] spawn IP_fnc_simpleSentence;

Then you have the wrong order of parameters in that task creation. It should look like this:

[player, ['tReturn','tHideandSeek'], ['Take Samuel back to <marker name=''mReturnmax''> Max </marker>.', 'Return to Max.', 'Deliver Samuel to Max.'], 'mReturnmax', nil, [b][color="#800000"]1, false[/color][/b]] call BIS_fnc_taskCreate;

3. There are different ways to call code. Either by execVM, call or spawn. There's plenty of material on the forums or in KillzoneKid's blog for example so I'll just give you the gist:

  • execVM - Reads, compiles and then executes text file in a separate thread.
  • call - Calls already compiled code in the same thread.
  • spawn - Calls already compiled code in a spearate thread.

So, many coders nowadays advise against execVM because it's rather inefficient as it works like a classic line interpreter, means it takes extra time on execution (because of compiling) and it really only makes sense if you run a script one time only. Hence, I also advised you to pre-compile your script. That can be done in different ways, we'll just look at the two most common. I'm just taking a totally unrelated example from one of my missions here right out of any context.

Either have in your init.sqf:

IP_fnc_spawnPlane = {	
_pos = getMarkerPos "mCSATAirSpawn0";
_dir = markerDir "mCSATAirSpawn0";
_grpPlane = createGroup east;
_plane = createVehicle ["O_Plane_CAS_02_F", _pos, [], 0, "FLY"];
[_plane, _dir] call IP_fnc_setDirFly;
[_plane, _grpPlane] call BIS_fnc_spawnCrew;
};

And call it with:

call IP_fnc_spawnPlane;

Or have in your init.sqf:

IP_fnc_spawnPlane = compileFinal(preprocessFileLineNumbers "spawnPlane.sqf");

And then have in an extra file called "spawnPlane.sqf":

_pos = getMarkerPos "mCSATAirSpawn0";
_dir = markerDir "mCSATAirSpawn0";
_grpPlane = createGroup east;
_plane = createVehicle ["O_Plane_CAS_02_F", _pos, [], 0, "FLY"];
[_plane, _dir] call IP_fnc_setDirFly;
[_plane, _grpPlane] call BIS_fnc_spawnCrew;

Finally, execute somewhere via:

call IP_fnc_spawnPlane;

As for your fear of having too many files: Organise them in folders or get your self acquainted with FSMs (Finite State Machines), i.e. the "missionFlow.fsm". However, that might come later, if I were you I'd try to get the SQF basics first.^^

Share this post


Link to post
Share on other sites

Awesome thanks Indeed! KillzoneKid's blog is crazy thanks man!

So the rest action is set correctly and I tried to spawn a unit that appears in the morning from 5 to 7.

its a civilian unit named Sam with dialogue. the classname ive found was C_man_1, when trying to spawn Sam, it just gives me a random civ. Any way of spawning Sam?

Edited by Xabialonso

Share this post


Link to post
Share on other sites

Depends. Should your Sam always look the same? Google for CfgIdentities to see how to set up face, voice etc. permanently. Scripted solution could look like this:

_pos = [0, 0, 0]; // ToDo: Insert Position!
_grp = createGroup civilian;
"C_man_1" createUnit [_pos, _grp, "Sam = this; this setVariable ['BIS_enableRandomization', false];"];
Sam setName "Sam";
Sam setFace "WhiteHead_01"; // ToDo: Enter Face!
removeHeadgear Sam; 
removeGoggles Sam;
// Sam addGoggles "G_Aviator";
// Sam addHeadgear "H_Beret_Colonel";

Share this post


Link to post
Share on other sites

Tried the suggested script, he spawns fine and the cinematic spawn text appears when player approaches unit, however the player cant access the dialogue choices from the action menu.

heres what was tried:

CfgIdentities---- Sam setIdentity "MyLittleSoldier"; (the correct name pops up but no action access)

changing Sam setName "Sam" to---- this setName "Sam Villa", "Sam", "Villa";

Looked in the mission.sqm and saw how the other units that start at the beginning of the mission have their Edit Unit NAME option as text:IP_Buddy. Is it possible to set that Name option on units that haven't spawned at the beginning of the mission?

Share this post


Link to post
Share on other sites

If you used my snipped it's already set. Look closely, third line of code in my post above. "Sam = this; this setVariable ['BIS_enableRandomization', false];" is what's called in his init. So, you can access him by Sam or change that name as you like.

Share this post


Link to post
Share on other sites

Right. cpy n pasted but nothing. heres what i got in the init:

Samspawn = {

_pos = getMarkerPos "s"; // ToDo: Insert Position!

_grp = createGroup civilian;

"C_man_1" createUnit [_pos, _grp, "Sam = this; this setVariable ['BIS_enableRandomization', false];"];

Sam setName "Sam";

Sam setFace "WhiteHead_01"; // ToDo: Enter Face!

removeHeadgear Sam;

removeGoggles Sam;

// Sam addGoggles "G_Aviator";

// Sam addHeadgear "H_Beret_Colonel";

};

Sam setVariable ["IP_LiveFeed", true];

[sam, "Samopener"] call IP_fnc_addConversation;

[] spawn {

waitUntil {player distance Sam < 5};

[

[sam, player],

[

"Come here!"

],

"DIRECT",

true

] spawn IP_fnc_simpleConversation;

Sam say "resa"; // say sound - say3D might be working as well

sleep 2;

and in the missionsConvo.hpp

//sam

class Samopener

{

expression = "Sam say 'res';"

responses[] = {"lad","land"};

sentences[] = {

"What?",

"I said come here!"

};

};

Share this post


Link to post
Share on other sites

You're trying to use script commands on a unit that's not existent yet.^^ You need to spawn Sam before adding a convo or spawning code.

Share this post


Link to post
Share on other sites

Alright got it thanks Pete.

Can we set the amount of time the dialogue box appears? Tried this

[] spawn {
waitUntil {player distance thugs < 5};
thugs say "thugs"; 
[thugs, "enter text enter text", [color="#FF0000"]4[/color] "DIRECT",true] call IP_fnc_simpleSentence; 
[thugs, "enter text enter text", [color="#FF0000"]2[/color]"DIRECT",true] call IP_fnc_simpleSentence; 

};

and this

[code][] spawn {
waitUntil {player distance thugs < 5};
[
[thugs],
[
["enter text enter text",[color="#FF0000"]4[/color]],
["enter text enter text",[color="#FF0000"]2[/color]],
],
"DIRECT",
true
] spawn IP_fnc_simpleConversation; 
};

but the text takes a while for the next one to play.

Share this post


Link to post
Share on other sites

Your parameters are messed up. Check the function headers in my first post to see what parameters are accepted by which function:

/*
Name: simpleConversation
Author: IndeedPete
Purpose: Wrapper function to create a simple conversation from several simple sentences (IP_fnc_simpleSentence).
----------
Parameters:
_speakers - ARRAY OF OBJECTS: Units that alternately say the _sentences. - [player, SomeGuy]
_sentences - ARAAY OF STRINGS: Sentences the _speakers should say. Every Xth sentence will be spoken by the Xth speaker. - ["Hello world!", "Fuck off!"]
_wichChat - STRING (OPTIONAL): Which way of communication should be used. Available chats: "SIDE", "GROUP", "VEHICLE", "CUT" or "DIRECT" - "DIRECT" - DEFAULT: "SIDE"
_isCutscene - BOOL (OPTIONAL): Cutscene mode: If set to true cinema borders will show up and the conversation can be skipped by the player pressing space. - true - DEFAULT: false
----------
Requires:
IP_fnc_simpleSentence
*/

/*
Name: simpleSentence
Author: IndeedPete
Purpose: Simulate radio or direct conversations between units. Calculates delay based on input length. Defines global variable "IP_SimpleSentence_Talking" while someone is talking.
----------
Parameters:
_speaker - OBJECT: Unit that should say the _sentence. - player
_sentence - STRING: Text that the _speaker should say. - "Hello world!"
_wichChat - STRING (OPTIONAL): Which way of communication should be used. Available chats: "SIDE", "GROUP", "VEHICLE", "CUT" or "DIRECT" - "DIRECT" - DEFAULT: "SIDE"
_add - NUMBER (OPTIONAL): Addition in seconds to the calculated delay. Can be negative. - 5 - DEFAULT: 0
_len - NUMBER (OPTIONAL): Fixed delay. - 20 - DEFAULT: ((count (toArray _sentence)) * _x) <- calculation to determine delay
----------
Requires:
Dialog "IP_DLG_SIMPLESENTENCE"
.jpg - "conv\img\defaultAvatar.jpg"
*/

Share this post


Link to post
Share on other sites

Great thanks man! Hey Pete so in the mission there are NPCs that can become followers.

1.Is it possible to have the followers say random chatter when idle?

2. Can we set more than one opener and have them be called randomly?

Share this post


Link to post
Share on other sites

1. Yes, with some extra scripting.

2. Sure you can add multiple convos, random answers are not supported yet but again, some scripting could do it.

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

×