Callbacks and TRNG Services

Summary

          Introduction
          Trng, your plugin and the others...
                    But what means "linked"?
                    Why should I call trng services?
          The Service() function
          What happens when your code calls a trng service
          What is a Callback?
          Positional Callbacks and Replacing Callbacks
                    Positional Callback
                    Replacing Callback
                    Difference between Positional and Replacing callbacks
          First and After Callbacks
          What is better choice: First, After or Replace?
                    When to use CBT_FIRST
                    When to use CBT_AFTER type
                    When to use CBT_REPLACE type
                    When to use both CBT_FIRST and CBT_AFTER types



Introduction

Your plugin will comunicate with tomb_nextgerantion.dll (that we'll call "trng" to save time) with two methods:

  1. TRNG Services

  2. Callbacks functions


In this document you can get some infos about how these two comunications work.


Trng, your plugin and the others...


When the tomb4 program will be launched, it will load the "tomb_nextGeneration.dll" and then code in this dll (we name it "trng") will load all plugins it finds in trle folder.
To recognize the plugin files from other files, trng will parse for search mask "Plugin*.dll" in current trle folder.
All files that will match with this research will be loaded in memory and "linked" with trng.


But what means "linked"?
There are two kinds of links: the Services and the Callbacks.

Everytime the code of your plugin calls a function (a group of instructions with a specific function) present in trng, this is a "service".
Everytime the code of trng will call a function of your plugin, this is a callback.


Why should I call trng services?
In trng there is all code about script.dat, savegame, level data and triggers management.
Many times your code will need of some of these functions and in this situation you can call directly a function of trng using a service.



The Service() function

The service() function is a low-level function to call code present in tomb_nextgeneration.dll from your plugin.
In the reality it's seldom you need to use directly this function because you can use some front-end functions with easier names and arguments to perform some target and it will be in these front-end functions that there will be a call to Service() function.
Anyway if you are interested to kwow the back-stage of trng services this happens with this function.

The core of trng services is the function named Service():

// this is the procedure to call global trng services
int Service(DWORD SRV_Type, ...)
{
          va_list pArgs;
          
          va_start(pArgs, SRV_Type);          
          return Trng.Service(Trng.IdMyPlugin, SRV_Type, pArgs);
}




The code of this function is a bit misterious, but it is only because this function accepts a variable number of arguments (input values).
To manage a undefined long list of input arguments it's necessary "va_start()" C++ function.
Anyway it's more useful understanding as to use this function.
The first parameter is always a SRV_ constant name, and you can use the enum facilty "enumSRV." to see immediatly all availables SRV_ constants.
While the following paramaters may be different, as number and type, in according with the kind of SRV_ service.

Looking for "SRV_" text in "tomb_nextgeneration.h" source, you can see the list of SRV_ constants with some comment to describe the target and parameters to pass to it.

#define SRV_PERFORM_FLIPEFFECT 0 // you can use the shortcut function: PerformFlipEffect() to use this service
#define SRV_PERFORM_ACTION 1 // you can use the shortcut PerformActionTrigger() to use this service
#define SRV_PERFORM_CONDITION 2 // you can use the shortcut PerformConditionTrigger() to use this service
#define SRV_PERFORM_TRIGGERGROUP_ID 3 // Arg1=IdOfTriggerGroup
                                                            // return: -1 = not found Id
                                                            // return: 0 = condition is false
                                                            // return: 1 = condition is true or it's not a condition trigger group
#define SRV_PERFORM_EXPORTED_TRIGGER 4 // you can use shortcut function: PerformExportedTrigger() to use this service
                                                            // Arg1= PluginId / Arg2=FirstWord / Arg3=SecondWord / Arg4=ThirdWord
                                                            // return 0 = condition is false
                                                            // return 1 = condition is true or it's not a conditional trigger

#define SRV_CREATE_TRIGGERGROUP 5 // sequence of triple arg, {FirstWord, SecondWord, ThirdWord}
                                                            // with END_LIST value at end
                                                            // return -1 = error, missing available triggergroups
                                                            // return any other value = the id of your triggergroup


This is only an abstract of full list, of course.
The most of services have a front-end procedure that in above list has been named "shortcut function"
Since type everytime the Service() function, setting the wished SRV_ value and then typing the arguments (without having any help from Visual Express) it is a boring matter, for most used service it has been created a shortcut function to get easy the usage of that service.

A shortcut (or front-end) function it is simply another function, defined in "trng.cpp" source, that accepts only the required arguments, avoding the SRV_ constant, and then in this function it will be called the low-level function Service() to call that trng service.
For instance this is the shortcut for SRV_PERFORM_FLIPEFFECT function:

// perform an (existing) flipeffect trigger with the given data
bool PerformFlipeffect(char *pPluginName, int FlipNumber, int Arg1, int Arg2)
{
          int TimerField;
          int PluginId;

          if (Arg2==0) {
                    TimerField=Arg1;
          }else {
                    TimerField = Arg1 | (Arg2 << 8);
          }
          if (pPluginName==NULL) {
                    PluginId=0;
          }else {
                    PluginId=FindPluginID(pPluginName);
          }
          if (PluginId == -1) return false;
          
          Service(SRV_PERFORM_FLIPEFFECT, PluginId, FlipNumber, TimerField);
          return true;

}

In above code you can see that sometimes there are also extra code to prepare input argument in right way required from the service.

What happens when your code calls a trng service


In the image at left you can see what happens when your plugin call a service.
The service will dispatch your SRV_ value to specific trng function, it will perform that function and at end it will return the control to the code following the Sevice() call in your plugin code.
Pratically thanks to trng services you can call trng function like they were in your plugin.
Many service are not well documented because they work only like extern functions for some macro function from plugin code.
You can recognize this kind of service because their names begin with SRV_F_... text.
For instance you find in "tomb_nextgeneration.h" source you can see:

#define SRV_F_DisableSaving 37
#define SRV_F_AggiungiItemMosso 38
#define SRV_F_CheckForStartMovePushable 39
#define SRV_F_CheckForEndMovePushable 40
#define SRV_F_ExplosionOnVehicle 41
#define SRV_F_ConvertMicroUnits 42

That "_F_" is for "Function" of course, and above are services to call a function in trng that has not a standalone usage but it has been required from some more meaningful feature of your plugin.
In spite above service are not well documented you can always discover the parameters required simply looking for that service name in all sources of plugin and at end you'll find where it has been used and in this way you'll discover the kind and number of arguments passed to that service.
Anyway it's very seldom you'll find useful this little trng functions.



What is a Callback?

To introduce this argument and to explain better own the meaning of "callback" name we could do this "human" example:

There are two guys, they are friends and they work on same big project, but they are very far in different offices.
First friend, Bill, call (at phone) the second friend (Richard):
- Hi Richard, I need of something... When you'll begin to work about that budget about new reorganization, could you call me back? This is my number. Then you should say me some data about the budget limit, and I'll comunicate to you the requirement for my office

Richard agrees.

Above it is a (weird) example of a callback.
Bill is your plugin and Richard is trng.
When Bill does first call to Richard it is when your plugin from RequireMyCallBacks() function call the instruction:

          GET_CALLBACK(CB_INIT_OBJECTS, 0, 0, cbInitObjects);

In above code the "cbInitObjects" is the phone number that Bill gave to Richard to be recalled.
While the "CB_INIT_OBJECTS" it's the matter whom Bill is interested, the "budget"

When Richard (some time later) is going to work on budget, he will call (back) Bill to the phone number (function cbInitObjects()) that Bill gave to him in first phone call.
In this second call, Richard will pass some data about budget to Bill.
Bill will know these data (whereby input parameter of cbInitObject() function) and then he will change something or he will return his requirement to Richard.

In spite above example is off-topic, it explains fine the logic of callback.
Trng controls everything, and your plugin could be interested to handle some operation WHEN it will happen, but to do this it has to require to trng to be recalled (or receive a call-back)
When plugin ask a callback with GET_CALLBACK() function, trng will take note to have to recall that plugin when the wished operation is going to be performed.

The "when" is important, because in tomb raider game there are different moments and some operation will be possible only in right moment.
There is a phase when the game save the data of current level in savegame, and if your plugin wish save some of its data in savegame, it has to be recalled own in the moment of saving and not first, or after.
There is a moment when the objects will be loaded from tr4 file, and if you wish initialise or change something of them, you need that your function (callback) was called own immediatly after the loading of object.
If you wish draw characters, sprite, images or meshes on the screen, you'll be able to do ONLY in right moment, when the game is drawing on screen (between beginscene and endscene phases) otherwise your drawing will have no effect.
If you wish perform some code when a flipeffect has been triggered in game, you need that your function was called in the moment of triggering.
Ect. ect.


Positional Callbacks and Replacing Callbacks

In according with their nature and working mode the callbacks may be Positional or Replacing callbacks.


Positional Callback
You'll use a positional callback when you wish simply that your code (a function: your callback function) was called in a given point or moment of tomb/trng execution but you are not interest to change or replace the standard behavior of tomb/trng code where the call back will be called.
For instance these are some positional callbacks:

#define CB_INIT_PROGRAM 0 // DEFAULT. You have already it. Look for "cbInitProgram" procedure in Plugin_trng.cpp source
#define CB_SAVING_GAME 1 // DEFAULT. You have already it. Look for "cbSaveMyData" procedure in Plugin_trng.cpp source
#define CB_LOADING_GAME 2 // DEFAULT. You have already it. Look for "cbLoadMyData" procedure in Plugin_trng.cpp
#define CB_INIT_GAME 3 // DEFAULT. You have already it. Look for "cbInitGame" procedure in Plugin_trng.cpp source
#define CB_INIT_LOAD_NEW_LEVEL 4 // DEFAULT. You have already it. Look for "cbInitLoadNewLevel" procedure in Pluging_trng.cpp source

In above callback you wish that your callback was called while trng/tomb is doing some operation but you'll not change the original code, furtherly you'll add some code to the origjnal tomb/trng code.
The positional callbacks usually don't require any flag to pass at GET_CALLBACK() function, excepting the type and function name to call, of course.
Most callbacks are of Positional type.


Replacing Callback
You'll use a Replacing Callback when you wish using your code (of your callback function) to replace the original code from where your callback will be called.
When you wish a replacing callback you need to supply the CBT_REPLACE flag in GET_CALLBACK() function
For instance:

          GET_CALLBACK(CB_FLIPEFFECT, CBT_REPLACE, 102, cbFlip102);

In this case, above instruction it will require to trng to call your function cbFlip102() when the (trng) flipeffect number 102 will be triggered, in replace way.
That CBT_REPLACE, means that the original code of flipeffect 102 will be not performed but only yours, that in your cbFlip102() function.
Other Call Back Types, like CBT_FIRST and CBT_AFTER are instead Positional callbacks, since they will call your code but also the original code will be performed.
Not all CB_ callbacks accepts the CBT_FIRST, CBT_AFTER or CBT_REPLACE types.
When it's not reasonable replace the whole calling (original) code, that change will be neither supposed.


Difference between Positional and Replacing callbacks
Main difference between Positional and Replacing callbacks is that many plugins can have same kind of positional callback with no risk of conflicts, while for each specific callback type, only one plugin can have a Replacing callback.
For instance the CB_SAVING_GAME is a positional callback that trng will call when it is going to save the level data in savegame.
In that situation all plugins can have a call back of CB_SAVING_GAME type, since it's not a problem to call all callbacks of plugins in that moment.
But when the call back is of CBT_REPLACE type, only one plugin it will be able to replace the original code, because it's not possible having many instances and repeating of same (original) code.
In this situation the only plugin to get the right to have a replacing callback, it will be last plugin to have required that type of callback.
Since the plugin will be sorted by date, it will be the more recent plugin to be able to force its replace callback, furtherly toggling that chance to previous (older) plugin that did same request.

First and After Callbacks

Usually when a callback accepts the CBT_REPLACE type, it should accept also the CBT_FIRST or CBT_AFTER types.
The "first" or "after" are always in relation with original code.
For instance your plugin can require a callback for some trng trigger. This means that when the code of that trigger should be performed your plugin wish that its (callback) function was called.
If the callback had CBT_FIRST type, the callback of your plugin it will be called FIRST of original code, and then it will be performed also the original code.
If the callback had CBT_AFTER type, the original code will be performed and only AFTER its execution, it will be called also your callback.
While if the callback had CBT_REPLACE type, only your callback function it will be performed and the original code will be skipped.


In above image you can see the three types of callback and how they work.

What is better choice: First, After or Replace?

When should you set a FIRST callback, and when an AFTER or REPLACE callback?
It depends by your targets, of course, anyway we could suppose some situation where it's better one or other.


When to use CBT_FIRST
An interesting chance supplied by CBT_FIRST type, is that, in some situations, you can transform the FIRST in REPLACE callback but in conditional way.
Example:

; Set Trigger Type - ACTION 14
; Exporting: TRIGGER(782:0) for ACTION(64) {Tomb_NextGeneration}
; <#> : ANIMATING1 ID 64 in sector (2,4) of Room6
; <&> : Enemy. Kill <#>object in (E) way
; (E) : Exploding moveable underwater
; Values to add in script command: $5000, 64, $30E

Now we suppose that you wish affect the code of Action14 trng trigger (see above an example of this trigger)
But you don't wish replace all its code, because you should create a code to manage all different kinds of killing ((E)way parameter)
You wish only use your code when there is an underwater explosion, while you don't want handle all other situations, so in the case it was not an "underwater explosion" you let to original code to handle the trigger A14.
We could manage this situation in following way:
You require a callback of CBT_FIRST type for action trigger 14 (from RequireMyCallBacks() function):

          GET_CALLBACK(CB_ACTION, CBT_FIRST, 14, cbHandleAction14);

Now we create the function that will be called from trng when the action trigger 14 will be triggered in game:

int cbHandleAction14(WORD ActionIndex, int ItemIndex, WORD Extra, WORD ActivationMode, WORD CBType)
{

}

Note: we discovered the arguments required for this call back, reading the bottom of the "DefTomb4Funct.h" source file.
We found the generic decaration of its pointer and we got from that declaration the list of input parameters (enclosed in round parentheis) and the returned value (at left of the row immediatly after "typedef" instruction):

// for CB_ACTION callback
// CBType remember to you the kind of callback it was (CBT_FIRST, CBT_AFTER, CBT_REPLACE) in this way you could use
// same callback procedure to handle both types of all triggers callbacks
// return TRET_ mask values
typedef int (WINAPI *CALL_ACTION) (WORD ActionIndex, int ItemIndex, WORD Extra, WORD ActivationMode, WORD CBType);

Remark: if you don't find any information about declaration of some callback functions, it means that is a CALL_VOID function, i.e. a function where there is no argument and no returned value.
It happens very often for some positional callback. In that siutation your function will be:

void NameOfMyCallback(void)
{

}


Now we come back to our callback function for A14:

int cbHandleAction14(WORD ActionIndex, int ItemIndex, WORD Extra, WORD ActivationMode, WORD CBType)
{

}

The input arguments of this function gave to us many infos.
Some of them will be already know by us, for instance, in this situation it's objviuous that ActionIndex will be = 14, because we did a callback only for that action trigger.
Anyway we could ask for many callback for different action trigger numbers and give for each of them the same callback function. In this situation we'll use the ActionIndex parameter to discover what is the action trigger to manage in that moment.
ItemIndex will be the index (already in internal "tomb" format) for the moveable that will affect by this trigger, while the Extra value is that we'll use in our example, because it will the number to indicate the "(E)way" to kill that item.
From ngle program, in Set Trigger Type window, we can discover the numeric values of a parameter list, clicking on the little [P] (Parameter list) button at right of list for killing way, and you'll get a text file with the list of ways and their numeric values:

#ID DESCRIPTION
--------------------------------------------------------------------------------
7: Anti-trigger Item (like if it was never enabled)
0: Default death animation (only for mortal creatures, removing vitality)
8: Disable emitter
4: Exploding creature
2: Exploding moveable on ground/sky
3: Exploding moveable underwater
6: Hide Creature (when other ways don't work)
5: Kill Creature
1: Remove immediatly (disappear any item)
--------------------------------------------------------------------------------
Total Items = 9

For above list we discovered that the killing way about "exploding underwater" has value = 3.
So in our call back function we can type a condition about the Extra parameter value:

int cbHandleAction14(WORD ActionIndex, int ItemIndex, WORD Extra, WORD ActivationMode, WORD CBType)
{
          if (Extra != 3) return enumTRET.EXECUTE_ORIGINAL;

          // ... here we type our code, that it will work only when is has been chosen the "3: Exploding moveable underwater" way

          // at end we'll quit with returned value to skip the execution of originale code
          return enumTRET.PERFORM_ONCE_AND_GO;
}

In this way we'll get out target: to manager the situation about "Exploding moveable underwater" replacing original code with our code, while we'll let to original code the target to manage all other situations.

Note: remember that you should use this trick only in this way: when you wish manage a single (not the totality) of operations handled by a trng trigger.
Differently, It should be a bad way to work if you use the CBT_FIRST callback omitting always the TRET_EXECUTE_ORIGINAL value. In this case you'll have to use a CBT_REPLACE callback.


When to use CBT_AFTER type
The after callback is useful when you wish "add" some skill to an old trng skill and you need to work on a situation where the trng skill has been already applied.
For instance we suppose to wish improve this flipeffect trigger:

; Set Trigger Type - FLIPEFFECT 79
; Exporting: TRIGGER(256:0) for FLIPEFFECT(79) {Tomb_NextGeneration}
; <#> : Lara. (Move) Move Lara in LARA_START_POS with <&>OCB value in (E)way
; <&> : OCB= 0 of LARA_START_POS(1) in sector (1,1) of Room0
; (E) : Keep original sector displacement of Lara
; Values to add in script command: $2000, 79, $100

If your target is to add a special effect when lara move to target position, like smoke or an explosion, it's necessary you have the execution of your code (your callback) AFTER that trng had moved lara to target position, otherwise you should add your spcial effect on source position of Lara where it will be not visible.


When to use CBT_REPLACE type
If you mean perform so many changes to original code to prefer work from scratch you'll use the CBT_REPLACE type, in this way all original code will be skipped and you'll have to reproduce same targets of original code with your callback.


When to use both CBT_FIRST and CBT_AFTER types
There is some particular situation where you could wish to set two callback for some stuff, one FIRST and other AFTER.
This working mode is useful when you wish discover if the original code performed some operation that you wish remove or change.
In this case you'll check in FIRST callback what was the situation before performing the original code, then in AFTER callback you verify if there have been a change respect to situation saved in FIRST callback and that is the current situation, after the original code has been performed, and in the case it was true you can affect that operation in different ways.
For instance if you wish set a differen animation when lara jump outside from the boat, you can get a CBT_FIRST and another CBT_AFTER callback about Collision Management procedure for BOAT object, and then you'll check if in first callback lara was yet on the boat, and if the AFTER callback she has animation to jump out from the boat, you can with your code change the current animation of lara only in this moment, as you wish.