Scripting: Conventions – Arma Reforger
Jump to navigation
Jump to search
Lou Montana (talk | contribs) m (1 revision imported) |
(→Method: correction: dashes -> slashes) |
||
Line 128: | Line 128: | ||
=== Method === | === Method === | ||
* All methods must be separated using this sequence of characters: two | * All methods must be separated using this sequence of characters: two slashes followed by 96 dashes (see {{HashLink|#Example}})<syntaxhighlight lang="cpp"> | ||
//------------------------------------------------------------------------------------------------ | //------------------------------------------------------------------------------------------------ | ||
</syntaxhighlight> | </syntaxhighlight> |
Revision as of 21:22, 17 May 2022
Tag
Bohemia Interactive scripts are prefixed with SCR_ as internal convention - it is recommended that each developer entity has its own prefix. See the Tag page for more information.
File/Class
- File should be called TAG_MyObject.c
- A component must end with "component": TAG_ExampleComponent.c
- An entity must end with "entity': TAG_ExampleEntity.c
- Class should be called the same (without file extension):
- class TAG_MyObject
- class TAG_ExampleComponent
- class TAG_ExampleEntity
- Enum:
- name must be prefixed with a capital E, e.g EMyEnum
- values use all capital letters with words separated by underscores, e.g EMyEnum.VALUE_1
- The file should be located in the scripts
\Game directory - The use of plural is prohibited at the end of combined keywords: e.g SCR_NotificationsComponent → SCR_NotificationComponent
Method
- Functions/Methods naming uses PascalCase, e.g:
int ReturnNumber()
- Parameters are named with camelCase, e.g:
int ReturnNumber(bool canBeNegative)
Variable
{{Feature|informative|See ]] for more information.}
- Member variables are prefixed with m_, eventually one-letter type prefix, and use PascalCase, e.g:
Entity m_Entity; int m_iHealth; bool m_bIsEnemy;
- Global variables are prefixed with g_.
- Static variables are prefixed with s_ (constants are not), eventually a one-letter type prefix, and use PascalCase, e.g
protected static int s_iUnitsCount;
- Local variables and arguments (method parameters) use camelCase, e.g:
void MethodA() { int value = 42; string name = "John"; float result = MethodB(value, name); };
- Constant values use all capital letters with words separated by underscores (uppercase snake casing), e.g:
const int MAX_VALUE = 9999; // no 'i' type prefix static const int TOTAL = 10; // a constant does not need
Script
General
- Curly braces must always be on a new line - Enforce Script uses Allman style
- Variables and functions should be protected whenever possible (respecting OOP black box principle) unless they are intended to be exposed
- Getters/Setters: variables should be made protected and accessed through getters and setters (entry methods getting/setting the value)
class SCR_HumanComponent : ScriptComponent
{
private int m_iAge;
void SetAge(int age)
{
m_iAge = age;
PrintFormat("Age of instance %1 is now %2", this, m_iAge);
}
int GetAge()
{
return m_iAge;
}
}
Spacing
- Tabs are used for indentation - they are set to a size of 4 spaces in Script Editor
- A space is used before and after:
- a binary operator
- a foreach colon
- a class' inheritance colon
- A space is used after:
- if, for, foreach, switch, while keywords
- a for semicolon
- Spaces are used inside parentheses but not around their content
class SCR_HumanComponent : ScriptComponent
{
if (true)
{
}
for (int i = 0; i < 10; i++)
{
}
foreach (string item : stringArray)
{
}
switch (value)
{
case 42:
break;
}
while (true)
{
}
}
Method
- All methods must be separated using this sequence of characters: two slashes followed by 96 dashes (see Example)
//------------------------------------------------------------------------------------------------
- Documentation must be done with Doxygen support in mind, using the
//!
syntax (see Example) - Methods should be sorted in the following order (top to bottom):
- General methods
- EOnFrame
- EOnInit
- Constructor
- Destructor
//! A scripted entity
class SCR_ScriptedEntity : GenericEntity
{
//------------------------------------------------------------------------------------------------
//! Get the normalized direction vector at position A pointing to B
//! \param vectorA First position, direction origin
//! \param vectorB Second position, direction goal
//! \return The direction from A to B as a normalized vector
private vector GetNormalizedDirection(vector vectorA, vector vectorB)
{
vector dir = vectorB - vectorA;
return dir.Normalized();
}
//------------------------------------------------------------------------------------------------
//! Frame
override void EOnFrame(IEntity owner, float timeSlice)
{
vector direction = GetNormalizedDirection(owner.GetOrigin(), vector.Zero);
Print("OnFrame was called! Direction: " + direction);
}
//------------------------------------------------------------------------------------------------
//! Init
override void EOnInit(IEntity owner)
{
Print("Init was called!");
}
//------------------------------------------------------------------------------------------------
//! Constructor
void SCR_ScriptedEntity(IEntitySource src, IEntity parent)
{
SetEventMask(EntityEvent.INIT | EntityEvent.FRAME | EntityEvent.CONTACT);
SetFlags(EntityFlags.ACTIVE, true);
}
//------------------------------------------------------------------------------------------------
//! Destructor
void ~SCR_ScriptedEntity()
{
Print("Destructing SCR_ScriptedEntity");
}
}
Miscellaneous
- class instanciation with the new keyword must use parentheses:
SCR_Class myClass = new SCR_Class(); // correct SCR_Class myClass = new SCR_Class; // wrong
- same with arrays:
array<string> myArray = new array<string>(); // correct array<string> myArray = new array<string>;
Modability
Example
[EditorAttribute("box", "GameScripted/SomeFolder", "Description of this component", "-0.25 -0.25 -0.25", "0.25 0.25 0.25", "255 0 0 255", "0 0 0 0", true, true, true)]
class SCR_SomeComponentClass
{
}
SCR_SomeComponentClass SCR_SomeComponentSource;
//! Flags used for an entity to define its currently active components.
enum SomeFlags
{
MESH = 1,
BODY = 2,
HIERARCHY = 4
NET = 8
}
//! A brief explanation of what this component does.
//! The explanation can be spread across multiple lines.
//! This should help with quickly understanding the script's purpose.
class SCR_SomeComponent : ScriptComponent
{
//! Defines the minimum distance (in metres) for this object to render. If below this value, object will be culled.
const float RENDER_DISTANCE_MINIMUM = 10;
//! Defines the maximum distance (in metres) for this object to render. If above this value, object will be culled.
const float RENDER_DISTANCE_MAXIMUM = 100;
//! Defines the maximum distance at which this object will be rendered in metres.
[Attribute("30.0", UIWidgets.Slider, "The maximum distance at which this object will be rendered in metres.", "0 120 0.1")]
private float m_fRenderDistance;
//! Maximum count of children that can be spawned at any time. If the limit is exceeded no more children are spawned.
[Attribute("100.0", UIWidgets.EditBox, "Maximum count of children that can be spawned at any time. If the limit is exceeded no more children are spawned.", "0 500 1")]
private int m_iMaximumChildCount;
//! The offset of this object in metres.
private vector m_vPositionOffset = "0 0 0";
//! A public variable
float m_fSomethingPublic = 3.2;
//! A public vector
vector m_vOtherPublic = "1 2 3";
//------------------------------------------------------------------------------------------------
//! Returns the render distance of this object (metres).
float GetRenderDistance()
{
return m_fRenderDistance;
}
//------------------------------------------------------------------------------------------------
//! Set the render distance of this object.
//! \param renderDistance Distance in metres. Is clamped between RENDER_DISTANCE_MINIMUM and RENDER_DISTANCE_MAXIMUM.
void SetRenderDistance(float renderDistance)
{
m_fRenderDistance = Math.Clamp(renderDistance, RENDER_DISTANCE_MINIMUM, RENDER_DISTANCE_MAXIMUM);
}
//------------------------------------------------------------------------------------------------
//! Prints hello to the debug console. Private function, not exposed to outside classes.
private void SayHello()
{
string localString = "Hello!";
Print(localString);
}
//------------------------------------------------------------------------------------------------
//! Compare two integers, return the larger one. (Don't mind the silly documentation, mind the syntax)
//! \param a First parameter to be compared with the second one
//! \param b Second parameter to be compared with the first one
//! \return Returns true if a is equal to b, false otherwise.
bool IsEqual(int a, int b)
{
// can be simplified to "return a == b;"
if (a == b)
return true;
else
return false;
}
//------------------------------------------------------------------------------------------------
override void EOnInit(IEntity owner)
{
Print("Initialized some component!");
}
//------------------------------------------------------------------------------------------------
void SCR_SomeComponent(IEntityComponentSource src, IEntity ent, IEntity parent)
{
ent.SetEventMask(EntityEvent.INIT);
// If offset is 0, no need to update
if (m_vPositionOffset != "0 0 0")
{
// Get current transformation matrix, add the position offset and update transformation.
vector mat[4];
ent.GetTransform(mat);
mat[3] = mat[3] + m_vPositionOffset;
ent.SetTransform(mat);
}
}
//------------------------------------------------------------------------------------------------
void ~SCR_SomeComponent()
{
}
};