Torque3D
  • General
    • Welcome!
    • Features
    • Release Notes
      • Version 4.0.3
      • Version 4.0.2
      • Version 4.0.1
      • Version 4.0
      • Version 3.10.1
      • Version 3.10
      • Version 3.9
      • Version 3.8
      • Version 3.7
      • Version 3.6.2
      • Version 3.6.1
      • Version 3.6
  • Getting Started
    • Introduction
      • What's the Torque3D Engine?
    • Getting Familiar
      • Getting a Copy
        • Torque Project Manager
        • Downloading it Yourself
      • Getting Ready for Launch
        • Running Pre-Built Binaries
        • Building the Engine Yourself
      • Launching the Game
        • Opening the Example Level
        • Launching the Editors
      • Your First Game
        • Introduction To The Engine
        • The Module System
        • Creating an empty gamemode
        • Adding a Player
        • Adding a Coin
        • Adding a win-condition
        • Custom coin asset
        • Counting coins
        • Adding some effects
        • Supporting multiplayer
        • Adding a Scoreboard GUI
        • Keeping the scoreboard up-to-date
      • Deep Dive: BaseGame Directory Structure
    • Best Practices
    • Porting a Legacy Project
  • For Artists
    • Assets
      • What are Assets?
      • How to Create a New Asset
      • Working With Assets
      • Deep Dive: Creating a New Asset Type
    • Art
      • File Formats
      • 3D Art
        • Shape Specifications
        • Coordinates System
        • Mounting Shapes
        • Animation
        • Player Setup
        • Blender -> Torque3D Pipeline
      • 2D Art
        • Working with Adobe Substance
    • Animation
    • GUI
      • Loading and Initializing a GUI
      • Expanding a GUI via Script
      • How to Network GUIs
    • Materials
      • Material Mapping
      • Material Animation
    • Terrain
    • Shaders
    • Lighting
    • Audio
  • For Designers
    • Base Classes
      • SimObject
      • SimGroup
      • SceneObject
      • Scene
      • Datablocks
    • Game Classes
      • Creating an Object
      • Destroying an Object
      • Gameplay Scripting
        • Spawning an Object from Gameplay Code
    • Modules
      • What are Modules?
      • How to Create a New Module?
      • Making a Module do Things
      • Installing Existing Modules
        • Where to Get More Modules
    • Scenes and Levels
      • How to Create a New Level
      • How to Load a Level
        • Deep Dive: Level Loading Scripts
      • How to Edit Levels
        • Opening a Level in the Editor
        • Spawning Objects from the Asset Browser
        • Working with Scenes
        • Using SimGroups
        • Changing a Level's PostEffects
        • Deep Dive: LevelAsset Companion Files
    • Game Modes
      • Creating a New GameMode
      • Making a Level Use Your GameMode
      • Adding Gameplay Code to Your GameMode
    • AI
      • Navmesh
      • Objects
      • Scripting
    • Inputs
      • Inputs and Keybinds
        • ActionMap
        • Bind Functions
        • ActionMap Stack
    • Localization
    • Editors
      • Changing Editor Settings
      • World Editor
        • Scene Editor
        • ConvexShape Editor
        • Terrain Editor
        • Terrain Painter
        • Material Editor
        • Spline-Based Tools
          • Mesh Road Editor
          • River Editor
          • Decal Road Editor
        • Datablock Editor
        • Particle Editor
        • Decal Editor
        • Forest Editor
        • Navmesh Editor
        • Deep Dive: Creating Your Own Editor
        • Shape Editor
      • GUI Editor
        • Interface Details
  • For Programmers
    • Compiling the Engine
      • Setup Development Environment
        • SDK and Library Installation
        • Git
        • Cmake
        • Creating a Fork on Github
      • Create a Project
        • Creating a Project With CMake
        • Creating a Project With the Project Manager
      • Compiling
        • Compiling in Windows
        • Compiling in Linux
        • Compiling in MacOS
      • Building the Project Manager
    • Introduction
    • Code Style Guidelines
    • Expanding the Engine
      • Creating a New Object Class
      • Exposing Object Classes to Script
        • addProtectedField
      • Adding a New Library to the Engine
    • Major Components of the Engine
      • Core
        • Console
        • Platform
      • Audio
        • SFX
      • Rendering
        • GFX
        • Render Bins
      • Physics
        • Stock T3D Physics
        • Physics Wrapper
          • PhysX
          • Bullet
        • Classes
    • Rendering
    • Math
    • Networking
      • Client and Server Commands
    • Physics
    • Collision
    • Scripting
      • TorqueScript
        • What is TorqueScript?
        • Basic Syntax
        • Variables
        • Types
        • Operators
        • Control Structures
        • Functions
        • Objects
        • Module Interop
          • QueueExec
          • RegisterDatablock
          • CallOnModules
          • ModuleExec
        • API Reference
      • Other Languages
        • C-Interface
    • File Inputs/Outputs(I/O)
    • API Reference
Powered by GitBook
On this page
  • Understanding callOnModules
  • Calling callOnModules to Trigger the Callback
  • Handling the Callback in a Module
  • Example Usage
Edit on GitHub
Export as PDF
  1. For Programmers
  2. Scripting
  3. TorqueScript
  4. Module Interop

CallOnModules

In Torque3D, callback functions are used to respond to certain events or actions in the game. One type of callback function that can be utilized is the callOnModules function. This function allows modules to respond to arbitrary calls in a signal-notify-like pattern, without being tied to a specific object. In this article, we will explore how to use callOnModules in TorqueScript.

Understanding callOnModules

The callOnModules function is used to invoke a function in all loaded modules. This means that any module that has the specified function will be called when callOnModules is invoked. This is useful for creating signal-notify-like behavior in Torque3D.

callOnModules has several useful parameters to control the signaling behavior, as well as allowing arguments to be passed along for recievers.

function callOnModules(%functionName, %moduleGroup, %var0, %var1, %var2, %var3, %var4, %var5, %var6)

As you can see, it takes not only the functionName to be invoked, but a moduleGroup. Normally this would just be the "Game" group, but allows you to filter the invokes for just tools, or core, or mods.

It additionally supports several arguments that can be passed along to facilitate the function behavior.

Calling callOnModules to Trigger the Callback

Once you have the callback function in your module's namespace, you can use the callOnModules function to trigger the callback in all loaded modules. For example, to call the "onMySignal" function in all loaded modules, you can use the following TorqueScript code:

callOnModules("onMySignal");

This will invoke the "onMySignal" function in all modules that have it.

Handling the Callback in a Module

When the callback function is invoked in a module, you can handle it like any other function. For example, if you have a module named "MyModule" and want to handle the "onMySignal" function, you can add the following code to your module:

function MyModule::onMySignal(%this)
{
   // Handle the signal here
}

This code defines the "onMySignal" function in the "MyModule" module and handles the signal in the module.

Example Usage

This behavior is broadly useful, because it allows many different modules to handle a common invoke in another module.

For an example, suppose we have in our game items that can be picked up. Rather than having to hard-integrate our gameplay code to specifically call functions for the inventory module to handle the on-pickup event, we can have the on-pickup event utilize callOnModules, and other modules will be able to listen for that call.

For the concrete example:

function ShapeBase::setInventory(%this, %itemData, %value)
{
   if(!isObject(%this.inventoryArray))
   {
      %this.inventoryArray = new ArrayObject();
   }
   
   // Set the inventory amount for this datablock and invoke inventory
   // callbacks.  All changes to inventory go through this single method.

   // Impose inventory limits
   if (%value < 0)
      %value = 0;
   else
   {
      %max = %this.maxInventory(%itemData);
      if (%value > %max && %max != 0)
         %value = %max;
   }

   // Set the value and invoke object callbacks
   %currentAmount = %this.getInventory(%itemData);
   if (%currentAmount != %value)
   {
      %name = %itemData.getName();
      %invIdx = %this.inventoryArray.getIndexFromKey(%name);
      
      if(%invIdx == -1)
         %this.inventoryArray.add(%name, %value);
      else
         %this.inventoryArray.setValue(%value, %invIdx);
         
      %itemData.onInventory(%this, %value);
      %this.getDataBlock().onInventory(%itemData, %value);
   }
   callonModules("onSetInventory","Game", %this, %itemData, %value); //%this is the object-instance holding the item
   return %value;
}

In this example, our inventory system module handles having an object's inventory being set. Once the inventory change has occured, we use callOnModules to inform about the onSetInventory call, along with the relevant data.

In another module, in this case an Objectives module, we have the function that will be invoked:

function ObjectiveModule::onSetInventory(%this, %playerInst, %itemData, %currentAmount)
{
    %count = %this.Objectives.count();
    for (%i=0; %i<%count; %i++)
    {
        %ObjectiveTag = %this.Objectives.getKey(%i);
        if (%ObjectiveTag.collectionItem $= %itemData.getName())
        {
            %idx = %this.items.getIndexFromKey(%itemData.getName());
            if (%idx != -1)
                %this.items.setValue(%currentAmount, %idx);
            else
                %this.items.add(%itemData.getName(),%currentAmount);
                
	         for (%clientIndex = 0; %clientIndex < ClientGroup.getCount(); %clientIndex++)
	         {
		         %cl = ClientGroup.getObject(%clientIndex);
               updateObjectiveForClient(%cl);
            }
            
            if (%currentAmount >= %ObjectiveTag.collectionCount)
                completeObjective(%ObjectiveTag);
        }
    }
}

This allows the ObjectiveModule to listen for the onSetInventory event without needing to modify the inventory module's code to call to the objectives module directly.

This allows much more effective drop-in-and-go behavior for modules and allows different gameplay code to easily interop without a lot of manual work to integrate.

PreviousRegisterDatablockNextModuleExec

Last updated 2 years ago