Jump to content
rileyrazor

Help Creating Custom Module

Recommended Posts

I am currently attempting to create a custom module, based off the tutorial by Bohemia on the wiki, but I'm afraid there's a section I just don't quite understand.

My module seems to appear in game with all of the correct text boxes and etc., but I can't figure out how to get my line of code to run. I always get an error no matter how I put it.

This is the code I am trying to run with the module.

 

["Origin",["Audio", "Distance", 1]] remoteExec ["say3d",0,true];


Here is my config.cpp
 

class CfgPatches
{
	class blake_blakesModules
	{
		units[] = {"blake_moduleAudio"};
		requiredVersion = 1.0;
		requiredAddons[] = {"A3_Modules_F"};
	};
};
class CfgFactionClasses
{
	class NO_CATEGORY;
	class blake_modCat: NO_CATEGORY
	{
		displayName = "Blake's Modules";
	};
};
class CfgVehicles
{
	class Logic;
	class Module_F: Logic
	{
		class AttributesBase
		{
			class Default;
			class Edit;					// Default edit box (i.e., text input field)
			class Combo;				// Default combo box (i.e., drop-down menu)
			class Checkbox;				// Default checkbox (returned value is Boolean)
			class CheckboxNumber;		// Default checkbox (returned value is Number)
			class ModuleDescription;	// Module description
			class Units;				// Selection of units on which the module is applied
		};
		// Description base classes, for more information see below
		class ModuleDescription
		{
			class AnyBrain;
		};
	};
	class blake_moduleAudio: Module_F
	{
		// Standard object definitions
		scope = 2; // Editor visibility; 2 will show it in the menu, 1 will hide it.
		displayName = "Global Audio"; // Name displayed in the menu
		category = "blake_modCat";

		// Name of function triggered once conditions are met
		function = "blake_fnc_moduleAudio";
		// Execution priority, modules with lower number are executed first. 0 is used when the attribute is undefined
		functionPriority = 1;
		// 0 for server only execution, 1 for global execution, 2 for persistent global execution
		isGlobal = 1;
		// 1 for module waiting until all synced triggers are activated
		isTriggerActivated = 1;
		// 1 if modules is to be disabled once it is activated (i.e., repeated trigger activation won't work)
		isDisposable = 0;
		// 1 to run init function in Eden Editor as well
		is3DEN = 0;

		// Menu displayed when the module is placed or double-clicked on by Zeus
		curatorInfoType = "RscDisplayAttributeAudioModule";

		// Module attributes, uses https://community.bistudio.com/wiki/Eden_Editor:_Configuring_Attributes#Entity_Specific
		class Attributes: AttributesBase
		{
			// Arguments shared by specific module type (have to be mentioned in order to be present)
			class Units: Units
			{
				property = "blake_moduleAudio_Units";
			};
			// Module specific arguments
			class Origin: Edit
			{
				// Unique property, use "<moduleClass>_<attributeClass>" format to make sure the name is unique in the world
				property = "blake_moduleAudio_Origin";
				displayName = "Object"; // Argument label
				tooltip = "Object or Unit the sound originates from. Must be variable."; // Tooltip description
				typeName = "STRING"; // Value type, can be "NUMBER", "STRING" or "BOOL"
				defaultValue = "object"; // Default attribute value. WARNING: This is an expression, and its returned value will be used (50 in this case)
			};
			class Audio: Edit
			{
				displayName = "Audio File";
				property = "blake_moduleAudio_Audio";
				tooltip = "Name of the audio file to be played. Must be configured in description.ext.";
				// Default text filled in the input box
				// Because it is an expression, to return a String one must have a string within a string
				typeName = "STRING"; // Value type, can be "NUMBER", "STRING" or "BOOL"
				defaultValue = "audio";
			};
			class Distance: Edit
			{
				displayName = "Distance";
				property = "blake_moduleAudio_Distance";
				tooltip = "Distance the audio file can be heard from (Default 100).";
				// Default text filled in the input box
				// Because it is an expression, to return a String one must have a string within a string
				typeName = "NUMBER"; // Value type, can be "NUMBER", "STRING" or "BOOL"
				defaultValue = "100";
			};
			class ModuleDescription: ModuleDescription{}; // Module description should be shown last
		};

		// Module description. Must inherit from base class, otherwise pre-defined entities won't be available
		class ModuleDescription: ModuleDescription
		{
			description[] = 
			{ 
				"This module allows you to easily play audio across all connected clients in a global environment.", 
				"Sounds must be configured in description.ext."
			};							 // Short description, will be formatted as structured text
			sync[] = {"LocationArea_F"}; // Array of synced entities (can contain base classes)

			class LocationArea_F
			{
				position = 0; // Position is taken into effect
				optional = 0; // Synced entity is optional
				duplicate = 0; // Multiple entities of this type can be synced
				synced[] = {"Anything"}; // Pre-define entities like "AnyBrain" can be used. See the list below
			};
		};
	};
};
class CfgFunctions
{
	class blake
	{
		tag = "blake";
		class blake_modCat
		{	
			file = "\blake\BlakesModulesMain\blake_blakesModules\functions";
			class moduleAudio {};
		};
	};
};


I don't understand the actual script section of the tutorial well enough to know how to implement it into my module. I've been reading through all the wiki pages I could find on the things that are in here, but can't seem to wrap my head around it. Maybe I'm out of my depth here. Here's the example provided by Bohemia of the section I don't understand. ( I've gotten the module to call the script with success, just not run the desired code).
 

// Argument 0 is module logic.
_logic = param [0,objNull,[objNull]];
// Argument 1 is list of affected units (affected by value selected in the 'class Units' argument))
_units = param [1,[],[[]]];
// True when the module was activated, false when it is deactivated (i.e., synced triggers are no longer active)
_activated = param [2,true,[true]];
// Module specific behavior. Function can extract arguments from logic and use them.
if (_activated) then {
	 // Attribute values are saved in module's object space under their class names
	 _bombYield = _logic getVariable ["Yield",-1]; // (as per the previous example, but you can define your own.)
	 hint format ["Bomb yield is: %1", _bombYield ]; // will display the bomb yield, once the game is started
};
// Module function is executed by spawn command, so returned value is not necessary, but it is good practice.
true


Any help would be greatly appreciated, and please let me know if I can provide any other information.

EDIT: Realized I may have put this in the wrong category, mods can move if needed.

Edited by rileyrazor

Share this post


Link to post
Share on other sites

OK, about the BI example, this is the basic code for a module supposed to run a function at mission start. (is3den = 0 so without code running during the edition of this mission)

 

_logic = param [0,objNull,[objNull]];  // this logic is the support object of your module and you can use it, for variables or else in this script

 

_units is the result returned by the class units argument of the module. Just in case you want to script for specific behavior on them, but, truly, that is not really useful as you can script what you want in your sqf!

 

if (_activated) then {...} // here is the code/ execVM sqf   for your module

this is the condition by which the code will run at start if the module is activated (can be false if some synced triggers are not activated)

As you can see, in more complex module this condition is placed in case: "init" for the switch mode. Because the other modes, like "attributesChanged3DEN", concern the module behavior during the edition. In your case (is3den = 0), nothing like that (you are in init phase by default).


 

Spoiler

 

In your config.cpp, as far as I see

blake_blakesModules  is the name of your addon (cfgPatches)

belonging to "NO CATEGORY" (no problem) and display name is:  Blake's Modules

You don't need a cfg3den, as far as you don't try to modify the default attributes in 3den (of the module's menu).  Fine.

In class cfgVehicles ... class logic ... class modules , that seems fine also.

You named your class module: class blake_moduleAudio: Module_F and the display name is: Global Audio

 

 

 

Your attribute classes are fine: origin, audio, distance,

They are passed to the init code of the function blake_fnc_moduleAudio:  if (_activated) then {..} by their property
This function seems to me OK but I can't check the path.  Just write :

if (_activated then {hint "OK"}; //to check it.

 

now you can script for the passed variables (attribute class results) by:

if (_activated) then {

private _orig = _logic getVariable ["origin",player];  // the object must exist! your say3D command needs an existing object. defaultValue = "object"; has no sense here.
private _audio = _logic getVariable ["audio","audio"]; // avoid too many "audio" name. Confusion is possible between the variable and the name of sound

private _dist = _logic getVariable ["distance",100];
 

[_orig,[_audio, _dist, 1]] remoteExec ["say3d",0,true];  // from say3D [sound, maxDistance, pitch] should be OK as far as "audio" is a correct sound (should be defined in the cfgSounds)

};

 

Hope this help. I guess you are not writing such module, just to send a 3D sound... at start, with no condition.

 

EDIT is not the only class you can use! For example you can choose among values with COMBO:

class origin: Combo
              {
                property = "blake_moduleAudio_Origin";
                displayName = "Object for origin";
                tooltip = "Object or Unit the sound originates from. Must be an existing object/unit.";
                typeName = "NUMBER";
                defaultValue = "3";
                class Values
                {
                    class none            {name = "HP";     value = 0;};
                    class occur            {name = "Sentry";    value = 1;};
                    class sharp            {name = "Bob";    value = 2;};
                    class aggressive    {name = "Radio";     value = 3;};

                };
            };

 

Combos are easy to work with numbers as above. Now, in the function code:
private _orig = [HP,sentry,Bob,Radio] select (_logic getVariable ["origin",3]);  // where HP, sentry, bob, radio are existing in game.

 

 

 

 

  • Like 1

Share this post


Link to post
Share on other sites

Thanks so much for your help! I certainly understand it more now. I am still getting one error, however. 

This is what I have inside the fn_moduleAudio.sqf:

(I changed the default value of the audio class to "audiofile")
 

_logic = param [0,objNull,[objNull]];
_units = param [1,[],[[]]];
_activated = param [2,true,[true]];
if (_activated) then {

private _orig = _logic getVariable ["origin",player];  // the object must exist! your say3D command needs an existing object. defaultValue = "object"; has no sense here.
private _audio = _logic getVariable ["audio","audiofile"]; // avoid too many "audio" name. Confusion is possible between the variable and the name of sound
private _dist = _logic getVariable ["distance",100];

[_orig,[_audio, _dist, 1]] remoteExec ["say3d",0,true];  // from say3D [sound, maxDistance, pitch] should be OK as far as "audio" is a correct sound (should be defined in the cfgSounds)

};

Whenever I activate the trigger to activate the module, I get an error telling me that there is an invalid number on line 8. Perhaps you might have an idea what is causing this.

Share this post


Link to post
Share on other sites

22:24:38 Error in expression <t = _logic getVariable ["distance",100];

[_orig,[_audio, _dist, 1]] remoteEx>
22:24:38   Error position: <

[_orig,[_audio, _dist, 1]] remoteEx>
22:24:38   Error Invalid number in expression
22:24:38 File blake\BlakesModulesMain\blake_blakesModules\functions\fn_moduleAudio.sqf..., line 8
22:24:38 Error in expression <t = _logic getVariable ["distance",100];

[_orig,[_audio, _dist, 1]] remoteEx>
22:24:38   Error position: <

[_orig,[_audio, _dist, 1]] remoteEx>
22:24:38   Error Invalid number in expression
22:24:38 File blake\BlakesModulesMain\blake_blakesModules\functions\fn_moduleAudio.sqf..., line 8
22:24:52 A null object passed as a target to RemoteExec(Call) 'bis_fnc_objectvar'

This is what I could find

Share this post


Link to post
Share on other sites

How did you manage your attributes in the module (origin , audio, distance classes?)

Share this post


Link to post
Share on other sites

This is my attributes class, If that's what you're asking.

 

class Attributes: AttributesBase
		{
			// Arguments shared by specific module type (have to be mentioned in order to be present)
			class Units: Units
			{
				property = "blake_moduleAudio_Units";
			};
			// Module specific arguments
			class Origin: Edit
			{
				// Unique property, use "<moduleClass>_<attributeClass>" format to make sure the name is unique in the world
				property = "blake_moduleAudio_Origin";
				displayName = "Object"; // Argument label
				tooltip = "Object or Unit the sound originates from. Must be variable."; // Tooltip description
				typeName = "STRING"; // Value type, can be "NUMBER", "STRING" or "BOOL"
				defaultValue = "player"; // Default attribute value. WARNING: This is an expression, and its returned value will be used (50 in this case)
			};
			class Audio: Edit
			{
				displayName = "Audio File";
				property = "blake_moduleAudio_Audio";
				tooltip = "Name of the audio file to be played. Must be configured in description.ext.";
				// Default text filled in the input box
				// Because it is an expression, to return a String one must have a string within a string
				typeName = "STRING"; // Value type, can be "NUMBER", "STRING" or "BOOL"
				defaultValue = "audiofile";
			};
			class Distance: Edit
			{
				displayName = "Distance";
				property = "blake_moduleAudio_Distance";
				tooltip = "Distance the audio file can be heard from (Default 100).";
				// Default text filled in the input box
				// Because it is an expression, to return a String one must have a string within a string
				typeName = "NUMBER"; // Value type, can be "NUMBER", "STRING" or "BOOL"
				defaultValue = "100";
			};
			class ModuleDescription: ModuleDescription{}; // Module description should be shown last
		};

EDIT: Sorry just realized what you were actually asking. I had the origin set to a variable I gave an object in game. Audio file was set to a sound already configured in the CfgSounds that worked by using the script outside the module. Distance was set to 100.

Share this post


Link to post
Share on other sites

That's probably because your attributes for the object is stringed (typeName). There isn't so much choice for that.

Imho, other attributes are OK if you write 200 (example) as distance, and "audioFile" as audio. The typeName matches.

But if you write unit1 (example) in object's field, the module is waiting for string... as actually scripted. And you can't do anything with that, once module is initialized. You must pass the string then call compile it for retrieving the existing object.

Try:

private _orig = call compile (_logic getVariable ["origin","player"]);

The alternate solutions:

- remove this choice for object in module (simplest).

- script a combo as shown above (you are working with numeric values.

- Prefer a synced method between module and object(s). And write something like: _unitSync = synchronizedObjects _logic;
so _unitSync is an array of synchronized object(s)

 

 

  • Like 1

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

×