World Editor Tool – Arma Reforger

From Bohemia Interactive Community
Jump to navigation Jump to search
(Page creation)
(No difference)

Revision as of 17:34, 15 May 2024



This tutorial teaches how to create a World Editor-specific tool.

A World Editor tool appears in the Tools bar unlike plugins that appear in the plugins list.

  • Please read Workbench Plugin and World Editor Plugin before following this tutorial.
  • Tools do not support shortcut definition - the icon must be clicked.
    • Tools do not run on icon click; they require button definition (see Setup) for them to trigger an action.


Setup

  • Open Script Editor
  • In an addon, create a new script in WorkbenchGame/WorldEditor - name it TAG_TutorialTool.c (must end with Tool by convention)
  • Double-click the file to open it
  • Press Ctrl + T to use the Script Template plugin
    • In its window, select "Class Type: WorldEditorTool", leave the other fields blank/default
    • A World Editor tool skeleton is inserted.
  • In the WorkbenchToolAttribute, fill the description field with "Tool description and quick usage guide"
  • Make the plugin inherit from WorldEditorPlugin instead of WorkbenchPlugin
  • Uncomment the Run() method and write Print("It works!"); in it, then save the file (default Ctrl + S)
  • Reload Workbench scripts via Reload Scripts option located in Plugins → Settings (default shortcut: Ctrl + ⇧ Shift + R)
  • The TAG_TutorialTool tool should appear in the World Editor's Tools list, available in the top bar - click on the defined icon
  • The tool's panel appears, with Tool description and quick usage guide text and the "Run" button - click on the Run button
  • "It works!" gets printed in the output console.


WorldEditorTool API

See the WorldEditorTool class.

The WorldEditorTool API offers some interesting things:

  • the m_API variable is a direct access to the WorldEditorAPI instance
  • the GetModifierKeyState() and the On* events methods allow to intercept user input
  • the UpdatePropertyPanel() method allows to refresh the tool's panel in case its values are changed by script.


WorldEditorAPI API

See the WorldEditorAPI class as well as WorldEditorAPI Usage.


Example

In this example, we will use BaseWorld and WorldEditorAPI methods to detect entities in a radius around the cursor's world position, draw lines from the cursor to them, hide/show them on double-click Double Left Mouse Button, and delete them on Del keypress:

Editing entities implies to wrap the action(s) (on potentially multiple entities) with m_API.BeginEntityAction() / m_API.EndEntityAction();

this is mandatory and also allows to have multiple actions into one "step", that undo/redo (default Ctrl + Z / Ctrl + Y) can process.

See WorldEditorAPI Usage.

#ifdef WORKBENCH [WorkbenchToolAttribute(name: "TAG_TutorialTool", description: "Set the wanted radius, move the mouse to highlight found entities, then:\n- double-click to hide/show them\n- press DEL to delete them", shortcut: "", awesomeFontCode: 0xF188)] class TAG_TutorialTool : WorldEditorTool { [Attribute(defvalue: "30", params: "0.1 100 0.1")] protected float m_fRadius; [Attribute(defvalue: "1", desc: "If checked, the cursor stops on the hovered entity; otherwise it only considers the ground as an obstacle")] protected bool m_bCursorOnObject; [Attribute(defvalue: "0.5 1 0.5 0.25", desc: "Sphere colour")] protected ref Color m_SphereColour; [Attribute(defvalue: "1 1 0 1", desc: "Line colour")] protected ref Color m_LineColour; protected ref array<IEntity> m_aNearbyEntities; protected BaseWorld m_World; protected ref SCR_DebugShapeManager m_DebugShapeManager; //------------------------------------------------------------------------------------------------ protected override void OnMouseMoveEvent(float x, float y) { if (!m_World) return; vector cursorWorldPos; if (!GetCursorWorldPosition(x, y, cursorWorldPos)) return; // not on screen m_aNearbyEntities = GetNearbyEntities(cursorWorldPos, m_fRadius); m_DebugShapeManager.Clear(); m_DebugShapeManager.AddSphere(cursorWorldPos, m_fRadius, m_SphereColour.PackToInt(), ShapeFlags.NOOUTLINE | ShapeFlags.DEPTH_DITHER); DrawLinesFromPosToEntities(cursorWorldPos, m_aNearbyEntities); } //------------------------------------------------------------------------------------------------ protected override void OnKeyPressEvent(KeyCode key, bool isAutoRepeat) { if (key == KeyCode.KC_DELETE) DeleteEntities(); } //------------------------------------------------------------------------------------------------ protected override void OnMouseDoubleClickEvent(float x, float y, WETMouseButtonFlag buttons) { if (buttons == WETMouseButtonFlag.LEFT) ShowHideEntities(); } //------------------------------------------------------------------------------------------------ protected override void OnActivate() { m_World = m_API.GetWorld(); m_DebugShapeManager = new SCR_DebugShapeManager(); } //------------------------------------------------------------------------------------------------ protected override void OnDeActivate() { m_aNearbyEntities = null; m_World = null; m_DebugShapeManager = null; } //------------------------------------------------------------------------------------------------ protected bool GetCursorWorldPosition(float x, float y, out vector cursorWorldPos) { vector traceStart, traceDir; TraceFlags flags = TraceFlags.WORLD; if (m_bCursorOnObject) flags |= TraceFlags.ENTS; return m_API.TraceWorldPos(x, y, flags, traceStart, cursorWorldPos, traceDir) && cursorWorldPos != vector.Zero; } //------------------------------------------------------------------------------------------------ protected array<IEntity> GetNearbyEntities(vector centre, float radius) { TraceSphere traceSphere = new TraceSphere(); traceSphere.Start = centre; traceSphere.Radius = radius; traceSphere.Flags = TraceFlags.ENTS; return SCR_WorldEditorToolHelper.TracePositionEntitiesBySphere(m_World, traceSphere); } //------------------------------------------------------------------------------------------------ protected void DrawLinesFromPosToEntities(vector worldPos, notnull array<IEntity> entities) { foreach (IEntity entity : entities) { m_DebugShapeManager.AddLine(worldPos, entity.GetOrigin(), m_LineColour.PackToInt()); } } //------------------------------------------------------------------------------------------------ protected void ShowHideEntities() { if (!m_aNearbyEntities || m_aNearbyEntities.IsEmpty()) return; m_API.BeginEntityAction(); IEntitySource entitySource; foreach (IEntity nearbyEntity : m_aNearbyEntities) { entitySource = m_API.EntityToSource(nearbyEntity); if (!entitySource) continue; m_API.SetEntityVisible(entitySource, !m_API.IsEntityVisible(entitySource), false); } m_API.EndEntityAction(); } //------------------------------------------------------------------------------------------------ protected void DeleteEntities() { if (!m_aNearbyEntities || m_aNearbyEntities.IsEmpty()) return; m_API.BeginEntityAction(); foreach (IEntity nearbyEntity : m_aNearbyEntities) { m_API.DeleteEntity(m_API.EntityToSource(nearbyEntity)); } m_API.EndEntityAction(); } } #endif