WorldEditorAPI Usage – Arma Reforger

From Bohemia Interactive Community
Jump to navigation Jump to search

World Editor API allows for plugin/tool world operations like obtaining terrain resolution, but mostly for Prefab operations such as editing and saving a Prefab (called Entity Template in the API for historical reason – hence the .et extension) using BaseContainer references.

See the WorldEditorAPI class for in-code information.


General Usage

Game classes such as WorldEditorAPI, ScriptEditor etc must not be a strong ref script-side; on scripts reload, these references are not nulled and could point to anything.

class TAG_MyClass { WorldEditorAPI m_WorldEditorAPI; // wrong protected void Method() { // m_WorldEditorAPI.DoSomething(); WorldEditorAPI worldEditorAPI = GetWorldEditorAPI(); worldEditorAPI.DoSomething(); // correct } // many ways to get WorldEditorAPI protected WorldEditorAPI GetWorldEditorAPI() { int method = Math.RandomInt(0, 4); if (method == 0) { WorldEditor worldEditor = Workbench.GetModule(WorldEditor); if (!worldEditor) // safety return; return worldEditor.GetApi(); } else if (method == 1) { return ((WorldEditor)Workbench.GetModule(WorldEditor)).GetApi(); // one-lined method } else if (method == 2) { return SCR_WorldEditorToolHelper.GetWorldEditorAPI(); // this helper does it for you } else if (method == 3) // if the current class is a WorldEditorTool child, m_API is available { return m_API; // this reference is managed by Workbench itself } return null; } }

↑ Back to spoiler's top


Prefab Actions

Prefab actions allow editing Prefabs automatically, with or without user input.

Editing a BaseContainer using .Set* methods does not modify the Prefab itself; it modifies the spawned entity itself.

Proper Prefab editing always happens in WorldEditorAPI methods, such as:

WorldEditorAPI.SetVariableValue() WorldEditorAPI.ClearVariableValue() WorldEditorAPI.CreateObjectVariableMember() WorldEditorAPI.CreateObjectArrayVariableMember() // etc

Get Prefab

//! Load ResourceName into a Resource //! \param[in] resourceName the resourceName to load //! \param[out] resource it is important to keep a strong reference to the loaded resource, //! otherwise its BaseContainer/IEntitySource will be dropped by memory management //! \return the provided resourceName's IEntitySource protected IEntitySource GetPrefabFromResourceName(ResourceName resourceName, out Resource resource) { resource = Resource.Load(resourceName); if (!resource.IsValid()) { resource = null; return null; } return resource.GetResource().ToEntitySource(); }

Set Value

values are set as string through WorldEditorAPI in .et files. For example, a true bool value will be represented as 1 in a .et file, therefore "1" must be provided (see bool.ToString(true) for more information).

Prefab actions must be wrapped between EntityAction begin/end methods below – the Workbench will remind you of that anyway with a VME.
worldEditorAPI.BeginEntityAction(); // actions here worldEditorAPI.EndEntityAction();

Direct Value

The SetVariableValue method is used:

worldEditorAPI.SetVariableValue(myEntitySource, null, "m_bVariable", bValue.ToString(true)); // .ToString(true) to write it as "1" worldEditorAPI.SetVariableValue(myEntitySource, null, "m_fVariable", fValue.ToString()); worldEditorAPI.SetVariableValue(myEntitySource, null, "m_iVariable", iValue.ToString()); worldEditorAPI.SetVariableValue(myEntitySource, null, "m_sVariable", sValue); // no .ToString() for string worldEditorAPI.SetVariableValue(myEntitySource, null, "m_vVariable", vValue.ToString(false)); // .ToString(false) to write it as "0 1 2" and not "<0, 1, 2>"

Native Type Array

The SetVariableValue method is used, setting values with separating commas:

worldEditorAPI.SetVariableValue(myEntitySource, null, "m_aBoolArray", "0,1,0,0,1,1,0,0,0,1,1,0,1,1,1,1,0,1,1,1,0,1,0,1"); worldEditorAPI.SetVariableValue(myEntitySource, null, "m_aFloatArray", "1.1,2,3.14159"); worldEditorAPI.SetVariableValue(myEntitySource, null, "m_aIntArray", "1,2,3,4,5,6,-10"); worldEditorAPI.SetVariableValue(myEntitySource, null, "m_aStringArray", "value1,value2,value3"); // note: commas cannot be escaped, making string with commas NOT supported worldEditorAPI.SetVariableValue(myEntitySource, null, "m_aVectorArray", "0 1 0,0 0 1,1 0 0");

Object Creation

Two cases are identified: the object can either be a direct object property or an array item.

In the first case (direct object property), the CreateObjectVariableMember method must be used:

// assuming an object is myEntitySource.m_SubObject // public, protected or private, WorldEditorAPI can write any Attribute worldEditorAPI.CreateObjectVariableMember(myEntitySource, null, "m_SubObject", "SubObjectClassName"); // returns true if type is valid and the object was created


In the second case (array element), the CreateObjectArrayVariableMember method is used:

// assuming an object is an item of the myEntitySource.m_aSubObjects array worldEditorAPI.CreateObjectArrayVariableMember(myEntitySource, null, "m_aSubObjects", "SubObjectClassName", 0); // returns true if type and index are valid and the object was created // but... how to get the existing index? // we can use the BaseContainer.Get method on the entitySource // using BaseContainer methods is fine as long as it is for reading information, not set them BaseContainerList subObjectsArray = myEntitySource.GetObjectArray("m_aSubObjects"); int nextIndex = subObjectsArray.Count();

Object Value

To set an object's value, the path parameter, an array of ContainerIdPathEntry must be used:

// let's reuse the myEntitySource.m_SubObject situation // m_SubObject has a "m_sMessage" property we want to set // m_SubObject cannot be set as SetVariableValue's first argument, as it is not the root container (myEntitySource being its parent) worldEditorAPI.SetVariableValue(myEntitySource, { new ContainerIdPathEntry("m_SubObject") }, "m_sMessage", "Hello there");

For performance reason, the path can be cached in order to create array and objects for each property.
Here for the sake of examples and teaching, arrays are created on the fly.


An array item is accessed using the ContainerIdPathEntry's index parameter, to determine which item is targeted

// let's reuse the myEntitySource.m_aSubObjects array situation worldEditorAPI.SetVariableValue(myEntitySource, { new ContainerIdPathEntry("m_aSubObjects", 3) }, "m_sMessage", "Hello there"); // changes the fourth array item


A component is accessed the same way - using the "components" path:

worldEditorAPI.SetVariableValue(myEntitySource, { new ContainerIdPathEntry("components", 0) }, "m_sMessage", "Hello there"); // if the first component must be edited


Delete Value

worldEditorAPI.ClearVariableValue(myEntitySource, null, "m_sName"); // erases "m_sName" from this Prefab's definition (not from parents or children)


An array cannot be deleted this way. All its items should be removed through RemoveObjectArrayVariableMember usage instead:

BaseContainerList subObjectsArray = myEntitySource.GetObjectArray("m_aSubObjects"); for (int i = subObjectsArray.Count() - 1; i >= 0; --i) { worldEditorAPI.RemoveObjectArrayVariableMember(myEntitySource, null, "m_aSubObjects", i); }

Save Changes

Simply use the following:

worldEditorAPI.SaveEntityTemplate(myEntitySource);


Helpers

See also: