How to add new Triggers




Summary

          Introduction
          Sections of TRG file
          Meaning of section name
          Contents of the Sections
          Trigger Descriptions
          Remarks and Documentation for Trigger
                    Adding a Remark with #REMARK# tag
                    Adding further documentation with #START_DOC# and #END_DOC# tags
                    Linking external help files with #START_DOC# , #END_DOC# tags
          Description of the arguments used by Trigger
          Sections used for Arguments
Preset Arguments
          How to create auto-list of arguments. The #REPEAT# tag
          Syntax of #REPEAT# tag
Our first Trigger
          The number of the trigger
          Flipeffect 800: Experiments
          How to get visible our trigger in NGLE program
          Our new Trigger in NGLE program
          How to catch the activation of our triggers in game
          The cbFlipEffectMine() callback
          FlipIndex argument
          Timer argument
          Extra argument
          ActivationMode argument
          How to divide the code for each of our Flipeffects
          Returned value from cbFlipEffectMine() callback
          Meaning of TRET returned values
          TRET_PERFORM_ONCE_AND_GO constant
          TRET_PERFORM_NEVER_MORE constant
          TRET_PERFORM_ALWAYS constant
          TRET_EXECUTE_ORIGINAL constant
How to add a new Action trigger
          The cbActionMine() callback
          ActionIndex argument
          ItemIndex argument
          Extra argument
          ActivationMode argument
          Returned value from Action trigger callback
How to add a new Condition trigger
          The cbConditionMine() callback
          Important note about code of Condition Trigger
          ConditionIndex argument
          ItemIndex argument
          Extra argument
          ActivationMode argument
          Returned values from cbConditionMine() callback. The CTRET_ flags
          CTRET_EXTRA_PARAM flag
          CTRET_ON_MOVEABLE flag
          CTRET_IS_TRUE flag
          CTRET_PERFORM_ALWAYS flag
          CTRET_ONLY_ONCE_ON_TRUE flag
          CTRET_NEVER_MORE_ON_TRUE flag
          CTRET_PERFORM_ONCE_AND_GO flag
          CTRET_EXECUTE_ORIGINAL flag

Introduction

To create your new triggers you have to pass to NGLE (room editor) some infos about the description of the trigger: its internal number and its further arguments.
All these data will be typed in a text file with extension ".trg" (like .TRiGger) and with the same name of your plugin.
In spite that its extension it's not ".txt" (warning to avoid to save it as a common text file) the content will be only text.
For instance, the file name for trigger file of "Plugin_Trng", it will be: "Plugin_trng.trg"
To be read from NGLE program, it's necessary that your TRG file was in trle folder.

Sections of TRG file

Internal syntax follows always same rules.
The TRG file is splitted in different sections.
Each section will be opened with:

<START_SectionName>

and will be closed with:

<END>

It's better you followed the sorting suggested in your (empty) TRG file.
The current content of Plugin_Trng.trg file is this


;------------------ Section for flipeffect trigger: description of the trigger -------------------------------
<START_TRIGGERWHAT_9_O_H>

<END>

;type here the sections for arguments used by above flipeffects


;------------------- Section for Action triggers: description of the trigger -------------------------------------
<START_TRIGGERWHAT_11_T_H>

<END>

;type here the sections for argument of above action triggers

;------------------- Section for Condition triggers: descrption of the trigger -------------------------------------
<START_TRIGGERTYPE_12_T_H>


<END>

;type here the sections for arguments of above conditional triggers



Meaning of section name


Really it's not so important you understood the meaning of section names.
You could simply remember those few section names that you'll have to use over and over.
Anyway if you wish know the meaning, that's here a little description.


The format of section name is divided (by underscore _ characters) in 5 fields:

  1. START
    Fixed name for all openings of section.

  2. TriggerField (example: "TRIGGERWHAT" or "TRIGGERTYPE")
    A mnemonic constant to describe when this trigger will be showed. It will happen only when the (above) TriggerField will have (in Set Trigger Type window) the same value of Internal Number (following field)
    In tomb raider the fields of trigger structure are:

    • Trigger Type or activation mode (antipad, heavy, switch, trigger ect)
    • Trigger What (Object, Flyby, Secret, Camera ect)
    • Object to Trigger (the index of moveable, or camera ect)
    • Timer (value for timer)

    When you start a section with TRIGGERWHAT field name, it means that the list (of its contents), will be showed only when the Trigger What (Activation type) had same value than you set in following field (Internal Number, see belove)

  3. Internal Number (example 11)
    This number is a code to describe what kind of trigger or object to trigger it is. For instance What to Trigger could be 7 = FINISH, 9=FLIPEFFECT, 10=SECRET, 11=ACTION
    While TriggerType could be: 1=PAD, 2=SWITCH, 12=CONDITION ect.

  4. List Prefix (example: "O" "T" "E" or "B")
    In Set Trigger Type window there are different list or text box, the List Prefix inform in what of these list show the content of current section.
    O = Object to Trigger
    T = Timer (Parameter)
    E = (E)xtra parameter
    B = Buttons (extra parameter set for condition triggers, using values of activation buttons)

  5. Hide/Show (example: "H")
    This is optional, if you omit it, in the description will be showed also the number of trigger or value of the argument.
    Usually I set H to hide the number of trigger in the description, otherwise the alphabetic sorting could not work fine.


Contents of the Sections

Inside of any section, i.e. in the middle of pair "<START....>" and "<END>" rows, you'll type the description of the triggers or their argument following this format:

Value:Description

For instance:

23:Close all doors

In above example "23" will be the value and "Close all doors" the description.
You use a colons (:) character to divide value by description.
You have to type one and only one "value:description" for row.

Trigger Descriptions

You'll type the description of your new trigger in the corresponding section

If you wish add a new flipeffect trigger, you'll add its description (and number value) to contents of section beginning with:

<START_TRIGGERWHAT_9_O_H>

For instance:


<START_TRIGGERWHAT_9_O_H>
200: Remove distance fog immediatly
<END>


With above text you created a new Flipeffect 200.

To add a new Action trigger you'll type the new description inside of this section:

<START_TRIGGERWHAT_11_T_H>

For instance:


<START_TRIGGERWHAT_11_T_H>
240: Add flames to <&>enemy
<END>


With above text you added a Action Trigger 240.

When you want add a condtion trigger you'll write its description inside of section beginning with:

<START_TRIGGERTYPE_12_T_H>

For instance:


<START_TRIGGERTYPE_12_T_H>
180: The <#> Enemy is aiming Lara
<END>


In this way you added a new Condition trigger 180

Remarks and Documentation for Trigger

From PU-7 update, it's possible adding some further description for main trigger.
For "main" trigger we mean the description of flipeffects, actions or conditions, excluding the arguments of triggers.
Pratically you can add a Remark that will be displayed in a informative row when that trigger has been selected.


In red circle you can see the Remark informative line.


Adding a Remark with #REMARK# tag
To have a remark note for a given trigger you have to add at end of main trigger description the #REMARK# tag.
Example:

41:Trigger. (Camera) Activate <#>Camera or fixed camera with (E)Timer value #REMARK#To use only as exported trigger (click on [Trigger's Help] button for more infos)

Notes:
- It's not necessary adding blanks (spaces) first or after the #REMARK# tag.
- There is no prefixed limit about length of remark text, anyway it will be showed only the text that fits in Remark frame of the Set Trigger Type window.
- Please, don't use the Remark note like a continuation of trigger description. It's not advisable for some reasons:
1) The remark will be seen only when user selected that trigger but it's not present in combo list when user opens the combo box. This means that he could see only half descriptions of some trigger (if you used remark as a continuation of trigger description)
2) The [Find] button cann't work on Remark notes but only on main trigger description.


Adding further documentation with #START_DOC# and #END_DOC# tags
The chance to add a remark, seen in above chapter, it's useful for little notes, fitting on a single row but when you need to add a more descriptive documentation about that trigger usage, it's better using the couple #START_DOC# and #END_DOC# tags.
These tags allow a multiple line description, using this syntax:

41:Trigger. (Camera) Activate <#>Camera or fixed camera with (E)Timer value #REMARK#To use only as exported trigger (click on [Trigger's Help] button for more infos)#START_DOC#
This trigger, like Action 42, should be used only as exported triggers.
You should create a TriggerGroup in script, placing at first side the Action 42, to set the target, and then the Action 41 to activate the camera.
If you don't follow this method and you try to use Action 41 or Action 42 directly from level map, these triggers will not work.
#END_DOC#
42:Trigger. (Target) Set <#>Moveable as Target for camera or fixed camera

In above example we added also a #REMARK# note and we let the following A42 trigger main description, to show how the .trg file will appear.
I remind following syntax rules about DOCs and REMARK
  1. You are free to add both tags (#REMARK# and #START_DOC#) on same trigger, or less.
    This means that you can:
    - Setting only a #REMARK#
    - Setting only a #START_DOC# (omitting the #REMARK#)
    - Setting either tags
  2. When you use both tags, it's necessary respecting the right sorting.
    First you place the #REMARK#, then the remark note, and at end of the row, the #START_DOC# tag
  3. There is no limit about length of DOC body.
  4. In DOC body you can use many linees
  5. At end of DOC body you have to place always the #END_DOC# tag, alone on a single row

The [Trigger's Help] button will be showed only when there is an extra documentation for selected trigger.
I.e. when you set a #START_DOC# tag for that trigger.


In above image you can see in green circle the [Trigger's Help] button.
In this case it has been displayed because the selected trigger had extra documentation set with #START_DOC#
While with other triggers the button will be hidden.


Linking external help files with #START_DOC# , #END_DOC# tags
If you need to give a real long description for a trigger (like a tutorial), you can use a special syntax inside of DOC body.
If you type in DOC body a text like following:

@"SomeName.ext"

When the user will click on [Trigger's Help] button, NGLE will open the file you supplied in "@' directive.
For instance, if you type this trigger description:

1: Lara is correctly aligned with <#>PC animating while hit ACTION command#START_DOC#
@"demo_plugin_help.htm"
#END_DOC#

When the condition 1 (of above trigger) is selected in trigger window and user click on [Trigger's Help] button, the "demo_plugin_help.htm" file will be opened, using the default application for that kind of file type (and extension)

Some notes about external help files:
  1. You have to type really the quotes to bound the name. This means that this syntax:
    @MyHelp.html
    should be wrong and it will not work
  2. You should not using any path (for folders) but only the file name.
  3. The help file should be enclosed in your Plugin Setup files and NGLE will look for it, in the corresponding plugin subfolder in NG_Center folder.
    This means that, if your plugin is "plugin_shocked" and you installed correctly the plugin setup files in NG_Center program, in ng_center folder there will be a "Plugin_Shocked" subfolder with all your plugin setup files. Between them there should be also the help file you used in your .trg file.
  4. I discourage to use a simple .txt file as external help file, since in this situation you could simply adding that text, inside of .trg file, using in standard way the #START_DOC# and #END_DOC# tags.
    The external help file chance, it has been thought own to permit more descriptive file format as documentation, like: html, words, pdf or .rtf files.


Description of the arguments used by Trigger

A trigger very often requires some argument.
In trng triggers you have already seen many descriptions like this:


64:Text. Print ExtraNG <&>string on screen for (E) seconds


When you see above description for trigger, you'll find also, in Set Trigger Type window, a list for given arguments.
For instance, for above Flipeffect 64 (to print extra ng string), we'll see in a list, all extra ng strings where to choose that to print, while in another (E) list the different times for Seconds.

Since each argument could have different values, a list of values and their descritpion, also for this list we need of another section in TRG file.
Each trigger will have its own section, one for each argument kind that it required.

Sections used for Arguments

The section name of argument is a bit more easy to remember that for trigger description, anyway its format is very alike:

START_TRIGGERKIND_NUMBER_LISTPREFIX_HIDE

TRIGGERKIND may be: "FLIPEFFECT", "ACTION" or "CONDITION"
NUMBER is the trigger number, for instance in above example "64:Text. Print ExtraNG <&>string on screen for (E) seconds" it will be "64"
LISTPREFIX may be: "O", "T', "E" or "B" (see above description)
HIDE is "H" or it will be missing to show also the number of argument in its description.

It's more easy to do some examples, since the names are always the same (excepting the number of trigger)

For a flipeffect description like this:


<START_TRIGGERWHAT_9_O_H>
500: Move Lara's hair in <&>way
<END>


The section of its argument, could be this:


<START_FLIPEFFECT_500_T_H>
0: Remove changes on Lara's hairs
1: Move hairs to west
2: Move hairs to east
3: Move hairs upside
<END>



Same method for Action trigger or condition trigger but it will change the prefix for list type:


<START_TRIGGERTYPE_12_O_H>
113:The <#>Creature is touching (E)water
<END>


The section of its argument, could be this:


<START_CONDITION_113_O_H>
#MOVEABLES#
<END>

<START_CONDITION_113_B_H>
0:Low water, one click or less
1:Middle water, more than one click and less than 3 clicks
2:High water, greater or even than 3 clicks
<END>


To remember the name for each argument section, in according with the kind of trigger: flipeffect, action or condition, you should remember the texts used to describe the different fields in Set Trigger Type window and compare them with following table.


Sections to use for Arguments

In this table the "nn" it should be replaced with the number of trigger whom you are typing its argument.
Argument Type FLIPEFFECT ACTION CONDITION
(Object to trigger <#>) ... START_ACTION_nn_O_H
Example:
<START_ACTION_43_O_H>
START_CONDITION_nn_O
Example:
<START_CONDITION_14_O>
Timer (Parameter <&>) START_EFFECT_nn_T_H
Example:
<START_EFFECT_47_T_H>
... ...
(E)xtra START_EFFECT_nn_E_H
Example:
<START_EFFECT_48_E_H>
START_ACTION_nn_E_H
Example:
<START_ACTION_43_E_H>
START_CONDITION_nn_B_H
Example:
<START_CONDITION_14_B_H>





Preset Arguments

In previous examples we saw also an argument like this:

<START_CONDITION_113_O_H>
#MOVEABLES#
<END>

But what is "#MOVEABLES#"?
By default the list of possible values for an argument has same syntax of trigger description: a couple "value:description" but in when the argument requires a very long list and the program (NGLE) already knows this list, you can use a preset argument list to inform NGLE to use a well-know list.
For instance, the "#MOVEABLES#" is the preset list for "all moveable items of current level.
When NGLE will find "#MOVEABLES#", then it will expand that preset value with the real list of all moveables and the values will be the indices of each item.


Preset List for Arguments

The "Range" info is important because you can use a preset list only in Prefix List where it will be able to be fit.
In (E)xtra parameter for Action and Flipeffect triggers you can host only values between 0 - 127
In (E)xtra parameter for Condition trigger you can host only values between 0 / 31
In Timer parameter of flipeffect triggers you can host values between 0 / 255, or, if you do not use (E)xtra parameter in that flipeffect, until 0 / 32767
"Object to trigger <#>" field can host any index of ngle objects but this value will be then reduced to 0/1023 when the ngle index will be converted in tomb format.
For this reason you cann't host your constants with values higher than 1023 in Object field.
The list of ngle items (moveables, camera ect) may be hosted ONLY in Object field.
Note: when you see in the Range column, as max range value, the "*" sign, it means "the max index for that kind of ngle index".
Preset Code Range Description
#ADD_EFFECT_255# 0 / 255 List of ID for AddEffect= script command from 1 to 255
#ANIMATION_LIST_255# 0 / 255 List of animation indices from 0 to 255, for timer parameter of flipeffect trigger
#ANIMATION_LIST_32A# 0 / 31 List of animation indices from 0 to 31.
It will be fit also in (E)xtra parameter of condition
#ANIMATION_LIST_32B# 0 / 31 List of animation indices from 32 to 63.
You'll have to add +32 to value of parameter to get real index of second group.
#ANIMATION_LIST_32C# 0 / 31 List of animation indices from 64 to 95.
You'll have to add +64 to value of parameter to get real index of third group.
#ANIMATION_LIST_B# 0 / 255 List of animations for Lara, from 256 to 511
Note: you have to add to parameter the value +256 to get the real index of second group
#ANIMATION_RANGE# 0 / 39 List of animation ranges for animated textures
#BACKUP_LIST# 0 / 127 List of game backup from 1 to 127
#BIT_LIST# 0 / 31 List of bit values as flag mask.
Pratically in the list there will be texts like thee:
$00000001
$00000002
$00000004
To decode the value of this parameter and get newly the above bit flag, you'll use this code:

BitFlag = 1 << Value;

Where the "<<" operator move at left for Value times the value "1".
#BUTTONS_LIST# 0 / 63 List of different combination of trigger button enabled.
Each row will have a syntax like this:
"[1][2][3][4][5] [OneShot]"
But many of above button will be missing.
The value of this parameter is the bit mask of buttons, and you should verify the presence of some enabled button in this way:
if (Value & 0x001) {
// enabled button [1]
}

if (Value & 0x0002) {
// enabled button [2]
}
if (Value & 0x0004) {
// enabled button [3]
}
The one shot is:
if (Value & 0x0020) {
// enabled one-shot button
}
#CAMERA_EFFECTS# 0 / * List of all indices about CAMERA and FIXED CAMERA in the level
#CD_TRACK_LIST# 0 / 255 List of audio track CD of AUDIO folder
#CLICK_DISTANCE_32# 0 / 31 List of click number for distance. One click is 256 game units.
#COLLISION_CEILING# 0 / 127 List of clicks to move up/down collision
From 1 to 63 to decrease ceiling collision
from 64 to 127 to increase ceiling collision.
For values higher than 63 you'll have to subtract to parameter value 64

if (Value < 64) {
// reduce the collisione
MoveDownCollision= Value;
}else {
// increase the collsion
MoveUpCollision= Value-64;
}
#COLLISION_FLOOR# 0 / 127 List of clicks to move up/down collision
From 1 to 63 to decrease collision
from 64 to 127 to increase collision.
For values higher than 63 you'll have to subtract to parameter value 64

if (Value < 64) {
// reduce the collisione
MoveDownCollision= Value;
}else {
// increase the collsion
MoveUpCollision= Value-64;
}
#COLORS# 0 / 100 List of 28 colors. The max value of 100 will be used to reset fog bulbs. You cann't use this list in Button (E)xtra parameter
#DEGREES# 0 / 7 List of degrees from 0 to 360 with steps by 45 degrees
#FLY_BY_SEQ# 0 / * List of indices about sequence of flyby camera
#FLYBY_LIST# 0 / * List of all Flby camera of the level
#FMV_LIST# 0 / 127 List of IDs for FMV.
#FOG_DISTANCES# 0 / 240 List of values about distance in sectors, positive and negative values, in the range (-119 / +119)
To convert the Value of this parameter you'll use this code:
Distance= 120 - Value;
#FRAG_2x2#
#FRAG_3x3#
#FRAG_4x4#
0 / 15 The list will be a serie of value where each value identify a coordinate (x,y) on a ideal grid that divides a sector.
The formula to compute each value is:
Value = y + (x *4)
While to extract from a single value the pair of x,y coordinates, the code will be:
x = Value / 4;
y = Value % 4;
#HALF_CLICKS# 0 / 512 List of values where each value is an half of click, i.e. 128 game units.
#HALF_CLICKS_32# 0 / 31 List of half-clicks for distance. This list is enough little to be stored in Button (E)xtra parameter for conditions
#INVENTORY-ITEM-INDEX# 0 / 118 List of moveable that you may find in inventory: like puzzle items, flare, medipacks ect.
The value get from trigger will be an index between 0/117, to convert it to read moveable slot you have to use above index to access to Trng.pGlobTomb4->VetInventory[] array.

For instance, if Timer variable has the value from trigger, you can discover the slot using the code:
short MovSlot;

MovSlot= Trng.pGlobTomb4->VetInventory[Timer];
And now in MovSlot there will a common slot id of some inventory moveable.
#INVENTORY-ITEMS# 0 / ~500 List of SLOT names but only for items can be showed in Inventory
#KEYBOARD_MODE# 0 / 3 List of four conditions. It is a combination between states "Multi-shot/Single-shot" and "Active/Inactive"
The value of parameter should be checked in this way:
If (Value & 1) {
// ACTIVE
}else {
// INACTIVE
}

if (Value & 2) {
// MULTI-SHOT
}else {
// SINGLE-SHOT
}
#LARA_ANIM_SLOT# 0 / 127 List of SLOT about lara: skin, weapons, crowbar ect.
#LARA_OTHER_SLOTS# 1 / 31 List of Lara's slots, omitting the base lara with slot = 1.
This preset list used to perform a swap mesh with other lara's slots.
#LARA_POS_OCB# 0 / * List of all indices of LARA_START_POS items placed in the level, showed by OCB values
#MEM_INVENTORY_INDICES# 0 / 118 List of indices to access to an array of structures, where, for each index, there is some data for that inventory item.
You can access to this structure using this code:

Trng.pGlobTomb4->pAdr->pVetStructInventoryItems[Index].

Note: Since it is a list of structure, then you can type '.' (dot) to see the list of fields for that given structure.

The same indices get access to another array, about some flags related to given inventory item.
To access to these flag array you use this code:

Trng.pGlobTomb4->pAdr->pVetFlagsInventoryItems[Index]

Note, above is a BYTE array, so all flags for a given item will be stored in a byte (0/255)

To have better infos about these two arrays, use TrngPatcher:
1) Load the data tomb4 source with the command [Load->Tomb4 Data Source]
2) Then, in loaded source, look for "4AC068:" text, to find the structure array, while look for "4AC9B8:" text, to find the byte flag array.
#MEMORY_ANIMATION# 0 / 31 List of animation memory variables.
To access to structure of that variable you'll use the value of parameter like an index:

StrAdrMemory *pAdr;

pAdr = &Trng.pGlobTomb4->pVetMemoryAnimation[Value];
#MEMORY_CODE# 0 / 127 List of code memory variables.
To access to structure of that variable you'll use the value of parameter like an index:

StrAdrMemory *pCode;

pCode = &Trng.pGlobTomb4->pVetCodeMemory[Value];
#MEMORY_INVENTORY# 0 / 31 List of indices for Inventory memory variables.
You use the value of parameter as index of global vector:
StrAdrMemory *pAdr;

pAdr = &Trng.pGlobTomb4->pVetMemoryInventory[Value];
#MEMORY_ITEM# 0 / 127 List of memory item variables.
To convert the value of parameter to pointer to structure of that variabile you use this code:

StrAdrMemory *pMemory;

pMemory = &Trng.pGlobTomb4->pVetItemMemory[Value];
#MEMORY_SAVE# 0 / 127 List of Memory savegame fields.
The value is an index to record for each Memory Savegame variable.
You should access to this record in this way:

StrAdrMemory *pSave;

pSave= &Trng.pGlobTomb4->pVetMemorySavegame[Value];
// and now with pointer pSave-> you can access to all values about that memory address
#MEMORY_SLOT# 0 / 31 List of slot memory variables.
To access to structure of that variable you'll use the value of parameter like an index:

StrAdrMemory *pAdr;

pAdr = &Trng.pGlobTomb4->pVetSlotMemory[Value];
#MICRO_CLICKS# 0 / 127 List of micro clicks for distance.
This list gives the chances to set values in game units for distance, but only choosing between step by 8 game units.
To convert the value of this parameter in game units you use this computation:

GameUnits = (Value+1) * 8;
#MOVEABLES# 0 / * List of all moveable items already placed in current level
#NEGATIVE_NUMBERS# 0 / 127 List of negative numbers, from -1 to -128
To value of parameter will be positive, to convert it newly at its negative value you'll use this code:

NegativeNumber = Value - 128;
#NG_STRING_LIST_255# 0 / 255 List of first 256 indices of Extra NG strings
#NG_STRING_LIST_ALL# 0 / 9999 List of first 10000 NG extra string indices.
You can show this preset list only in full timer of flipeffect trigger, i.e. a flipeffect where you do not use (E)xtra parameteres and for this reason you can have as single argument a timer with values upto 32767
#PC_STRING_LIST# 0 / 255 List of PC strings
#PERCENTAGE# 0 / 30 List of percentage values.
First group of values (1 / 9) will be seen as 0.N %
For instance value "4" it will mean "0.4 %"
Second group of values (10 /18), it will be seen as percentage from 1% to 9%
Third group of values (19 / 28), it will be sees as percentage from 10% to 90% with step by 10% (10, 20, 30, 40, 50, 60, 70, 80, 90)
#PSX_STRING_LIST# 0 / 255 List of PSX strings
#RECHARGE_256# 0 / 210 List of values for increasing HP.
First group of values, between 1 / 200, it is an absolute value to increase health
Second group of values, between 201 / 210 it is a percentage with step by 10: 10,20,30,40,50,60,70,80,90
#ROOMS_255# 0 / 255 List of room indeces from 0 to 255
Note: the value of parameter is already the real tomb index, it doesn't require conversion from ngle to tomb index.
#SAME_OF#= Variable This directive works in different ways than others.
While other preset list force in specific way a given preset list, the #SAME_OF#= directive works like a link to duplicate (copy) some other parameter list, in current new parameter list.
For instance if we created this parameter list for flipeffect trigger 51 in its Timer field:

<START_EFFECT_51_T_H>
0: All keyboard commands
1: Up
2: Down
3: Left
4: Right
5: Duck
6: Dash
7: Walk
8: Jump
9: Action (and Enter)
10: Draw Weapon
11: Use Flare
12: Look
13: Roll
14: Inventory
15: Step Left
16: Step Right
17: Pause
18: Save the game (special)
19: Load the game (special)
20: Select weapon keys (all fast weapon selctors)
<END>

And then, in another trigger we need to supply same parameter list, but, for instance, for the flipeffect 400, we'll type a text like this:

<START_EFFECT_400_T_H>
#SAME_OF#=EFFECT_51_T
<END>

In above way, the parser we'll replace, inside of parameter data for flipeffect 400 the same parameter list we saw for parameter of flipeffect 51
This directive works only for previously typed parameter list, of course.
#SEQUENCE_128# 0 / 127 A list of growing values. It is enough short to be hosted in (E)xtra timer for flipeffect and actions but not for condition triggers.
#SEQUENCE_32# 0 / 31 List of growing values from 0 to 31. This list is enough limited to be hosted in Button field (E)xtra parameter
#SET_STANDARD_MESH# 0 / 98 List of indices to access to slot array.
To access to selected slot you use this code:

Trng.pGlobTomb4->pVetStandardSwapMesh[Index];

Note: in above list of slot there is no specific reason. Simply I chose a set of slot where I thought it was reasonable store a swap mesh outfit.
#SFX_1024# 0 / 1023 Indices for first 1024 sound sample effectc
#SINK_LIST#" 0 / * List of all indices of Sink effect in the level
#SLOT_MESH_MOVEABLES# 0 / 127 List of slot to use for mesh swapping.
The value of parameter will be an index to a vector with slot indices.
You get the slot value in this way.

SlotValue = Trng.pGlobTomb4->pVetSlotMeshMoveables[Value]
#SOUND_EFFECT_A# 0 / 255 List of sound sample effect of first group, from 0 to 255
#SOUND_EFFECT_B# 0 / 255 List of sound sample effect of second group: from 256 to 511.
Please, note that the value of argument it will be in the range 0 / 255 therefor to get the real index you'll have to add 256 to the argument value.
#STATE_ID_LIST# 0 / 127 List for state-id of lara.
#STATIC_LIST# 0 / * List of all indices of static items in the level
Note in the code you'll have to convert this static index from ngle format to double tomb indices
#STATIC_SLOTS# 0 / 159 List of "only-static" SLOT
Note that the value of this parameter will have 0 as first static slot item.
#STRING_LIST_255# 0 / 255 List of first 256 tomb string indices. To use for Timer parameter of flipeffect but it is not possible use it for (E)xtra parameter of Action or Condition
#SWAP_MESH_SLOT# 0 / 85 List of indices to access to a slot array to use for swapping mesh.
You access to given slot with this code:

Slot=Trng.pGlobTomb4->pVetMeshSwapSlot[Index];

Note: this selection of slot is a serie of MIP slot of many moveables. Since the _MIP version slot was not so used I thought to use this serie of slot to host mesh swapping outfits.
#TEX_SEQUENCE# 0 / 127 List of IDs for TextureSequnce= script command used for animated texture
#TIME_LIST_128# 0 / 127 List of seconds from 0 (endless?) to 127. It could be hosted in (E)xtra parameter of flipeffect and actions but not for Condition triggers
#TIME_LIST_32# 0 / 31 List of seconds from 0 to 31.
This list will may fit in Button (E)xtra parameter of condition
#TIMER_SIGNED# 0 / 127 List of values for timer, positive and negative in the range -64 / +63
First group of values, 1 / 63, are the positive values.
While second group of values: 64 / 127, are negative values (-1,-2 ect).
To decode the Value of parameter and get the Timer value, you have to use following code:

if (Value <= 63) {
Timer=Value;
}else {
Timer = 63-Value;
}
#TIMER_SIGNED_LONG# -512 / +511 Number of second to set trigger's timer.
This value can be positive or negative, anwyay to get valid signed value, from trigger management, you need to perform a test on absolute value you got from Timer (usually) input parameter, since this value will be always positive in the range 0 / 1023
To recognize and convert, further negative values, you should consider that max positive value for timer is +511, i.e. in hexadecimal 0x1FF.
This means that, if the value you read is greater than 511, then it should be seen as a negative value and convert it in this way:
if (Value <= 511) {
Timer=Value;
}else {
Timer = 511-Value;
}
#TRANSPARENCY32# 0 / 31 List of transparency intensity.
The values of this parameter are mutply by 4
For this reason you should use following code to convert them to real transparency intensity:

if (Value == 31) {
Transparency= 127;
}else {
Transparency = Value * 4;
}
#VAR_LONG_STORE# 0 / 16 List of Long store variables.
To get the real variable code you have to add to value of parameter the value 0x1c0

VariableCode = Value + 0x1c0;
#VAR_NORMALS# 0 / 255 List of codes of all numeric variables, excluding Store variables
The values of this parameter are the same you find in Reference Panel of NG_Center program in Variable Placefolder section.
#VAR_STORES# 0 / 159 List of code of Store variables.
The codes are the same of Variable Placefolders in NG_Center program
#VAR_TEXT# 0 / 55 List of code of Text variables.
The codes are the same of Variable Placefolders in NG_Center program
#WAD-SLOTS# 0 / ~500 The list of all SLOTs.
In the list we'll find values like "LARA", "LARA_SKIN", "MOTORBIKE", "SKELETON" ect


How to create auto-list of arguments. The #REPEAT# tag

Many argument list are simply a sequence of growing numbers (1,2,3,4,5...) with the same description for each.
For instance to give the list of AddEffect= command the preset list "#ADD_EFFECT_255#" it will create an argument list like following:

1: AddEffect= 1
2: AddEffect= 2
3: AddEffect= 3
4: AddEffect= 4
5: AddEffect= 5
6: AddEffect= 6
7: AddEffect= 7
8: AddEffect= 8
9: AddEffect= 9

When you wish an argument list like above, but with a different descriptive text, you can use the #REPEAT# tag.

Syntax of #REPEAT# tag

The general syntax of repeat command is divided by "#" characters.

#REPEAT#TextToShow#FirstValue#LastValue#FirstIndex

TextToShow field
-----------------------
Is the text that it will be used as description for each value. In this description will be showed also the growing number, from FirstValue to LastValue

FirstValue field
--------------------
This is first value, lower value, for this argument.
The value will be used as argument value but also it will be printed in the description

LastValue field
--------------------
This it will be last value of the sequence.
It is enclosed in the sequence

FirstIndex field
---------------------
This is an optional field, you can also omit it.
When you omit this field its default value will be the same of FirstValue field.

To understand the usage of (optional) FirstIndex field, we have to see newly the stynax of a parameter list for triggers:

1: Move up of clicks 1
2: Move up of clicks 2
3: Move up of clicks 3
4: Move up of clicks 4

Above is an exable of parameter list that we can get using a #repeat like following:

#REPEAT#Move up of clicks #1#4

The level builder will see only the text like "Move up of clicks 2", while our trigger will receive only the value at left of ":" colon character.
This first value of each row, is that we call "Index".
Usuually, by default, the value of first index will be the same we typed in FirstValue field and also the same it will be showed in descriptive side of the row, that it will be seen by level builder in Set Trigger Type window.
What's happen if we set a FirstIndex value in above #repeat#, for instance giving a value 0?

#REPEAT#Move up of clicks #1#4#0

We'll get following list:

0: Move up of clicks 1
1: Move up of clicks 2
2: Move up of clicks 3
3: Move up of clicks 4

In above case the first index will be "0" while in previous example it was "1".
In this last situation we'll have, in the code of plugin that read this trigger paraemter, remembering that we have to add +1 to parameter index to get the value seen by level builder in Set Trigger Type window.
But why should we create a complication like this?\
There is some circustance where this is useful.
We have always to remember that the range of value for trigger parameter is limited. For some parameters, like extra timer the range is 0 / 127, for extra button of condition is yet less: 0/31
For instance we can see the flipeffect to change the animation for lara:

; Exporting: TRIGGER(0:0) for FLIPEFFECT(80) {Tomb_NextGeneration}
; <#> : Lara. (Animation) Force <&>Animation (256-512) of (E)slot for Lara
; <&> : 256 Animation
; (E) : SLOT_000 LARA
; Values to add in script command: $2000, 80, $0

Since the timer parameter support only a range 0/255 but the animations of Lara are much more, this trigger use the trick to ask for an animation in the range (256/512) but really, the index passed to our trigger, it will be (as usual) only in the range 0/255
To realise above Flipeffect 80 using a repeat tag, we could using this syntax:

#REPEAT#Animation =#256#512#0

And in this way an (abstract) of list we'll get will be like this:

0: Animation = 256
1: Animation = 257
2: Animation = 258
3: Animation = 259
,,,
255: Animation = 511

In this way we'll have asked to level builder number greater than 255 but, practically, our trigger parameter, will have indices in normal range of 0/255
Then, our trigger, will have simply to add +256 to index returned by this trigger parameter, to get the animation to set in Lara.





Our first Trigger

Now try to create really our first trigger.
We could create a flipeffect trigger with a generic target: give to us the chance to perform some experiment in game when the trigger it will be enabled.

The number of the trigger

We have to choose the number of this first flipeffect.
The flipeffects have a range from 0 to 1023, while the action trigger and condition triggers have a range between 0 - 255
The easier choice it should be to assign "1" at our first flipeffect and then increase to 2,3,4,5 ect for future flipeffect trigger that we'll create.
The only reason to start from a bigger number it is to avoid ambiguity with trng triggers or with triggers of other plugins.
For instance if you, beginning from 1 and growing numbers, you have your flipeffect 7, when you'll speak about the Flip 7, it could seem the old flipeffect "Plays continuos earthquake sound effect and rumbler.", while when you reach the flip 64, your flip will have same number of very used trng trigger: "Text. Print ExtraNG <&>string on screen for (E) seconds"
This is not a great problem, anyway since trng triggers reached max number of 405 but the max value is 1023, if you used flipeffect from number 600 (for instance) when you, in your tutorial or messages on forum, will speak about flip602 it will be not necessary explaining always that it is not a trng trigger ect.
Anyway it's a your choice.
Now we suppose to begin from number 800, but it's only an example.

Flipeffect 800: Experiments

This flipeffect will be a way to enable our code only when we'll trigger something in game.
So its description could be:
"Experiments. Perform <&>experiment passing to it the (E)Value"
In this way with this single trigger we'll be able to perfrom until to 256 experimentis (the <&> parameter, from 0 to 255), and to give, when it's necessary a value as argument for other 127 subtypes of experiment ((E)xtra has a range 0 / 127)

How to get visible our trigger in NGLE program

How we saw in previous paragraphs of current document, we have to type some data in TRG file of our plugin to inform NGLE program about description, number and arguments of our trigger.

Note: now in this example we'll use the name "plugin_trng.trg"as trigger file, anyway if you changed the name of your plugin in other way, you'll have to use your new plugin name with ".trg" extension to have a valid .trg file for your plugin.

Open the "plugin_trng.trg" file and type in section for flipeffect the main description of our flip 800 trigger:

;------------------ Section for flipeffect trigger: description of the trigger -------------------------------
<START_TRIGGERWHAT_9_O_H>
800:Experiments. Perform <&>experiment passing to it the (E)Value
<END>

Now we have to create the section for values of argument "<&>experiment".
Remembering the names of Sections used for Arguments we'll have to create this section outside of that for trigger description.
Since with flipeffects the "<&>" parameters will be in "T" (timer) list, and the (E) parameter there will be in E (extra) list.
We'll add our two sections for arguments of flip 800 in this way:

<START_EFFECT_800_T_H>
1: Move the cube
<END>

<START_EFFECT_800_E_H>
#REPEAT#Value=#0#127
<END>

About our (E)xtra parameter, we have already explained about How to create auto-list of arguments. The #REPEAT# tag

Now we save the TRG file in TRLE folder, and then we'll have to verify if NGLE program loaded correctly our new trigger.

Our new Trigger in NGLE program

I remember to you, that NGLE program will check for TRG file only when it has been just launched. So, if NGLE was already running when you changed your TRG file, you'll have to close NGLE and the launch it newly to do it loads the TRG file newly.
Now from NGLE, load a project, and open the Set Trigger Type window.
Click on [Find Trigger Number] button and type "F800"
Probably you'll see only white data for this flipeffect but it is because you have not set your plugin as engine.
Now select your plugin name in "Plugin/Engine" field...




Now you should see above data.
Our new flipeffect 800 trigger has been loaded in NGLE program.

Note: if you want that [Find Trigger Number] button gave to you immediatly the right flipeffect for your plugin, you have to type the flip to search, typing also the name of your plugin, dividing the name from the trigger type with colons ":" character:
In our case, having as name "plugin_trng", we can type:
plugin_trng:F800

or

trng:F800

How to catch the activation of our triggers in game

Now we'll place our new trigger in some sector and we'll build the level, with [Exit and Play] button.
But there is yet no code to manage our flipeffect 800...
So, close the game, and start Visual Express 2010, we have to catch when our trigger will be activated.

The cbFlipEffectMine() callback



Now load the plugin sources and then move in "plugin_trng.cpp" source and choose the cbFlipEffectMine() function (see above picture)
This function will be called from trng engine, everytime a flipeffect trigger (of yours) will be activated in game.
The arguments of this function will give you the info about what is the flipeffect number and the values of its parameters.

int cbFlipEffectMine(WORD FlipIndex, WORD Timer, WORD Extra, WORD ActivationMode)


FlipIndex argument

The FlipIndex variable it will have the value of flipeffect number.
Now that we have only the flip 800, we'll be sure that FlipIndex will be = 800 but when we'll have many flipeffects it will be very useful the FlipIndex argument to perform different codes in according with the number of flipeffect.

Timer argument

In Timer variable there will be the value of <&> parameter of the trigger.
In our example now it is surely "1" because we have not added other chances.

Extra argument

In Extra variale there will be the value of (E) parameter of flipeffect.
In our example it could be a number in the range 0 / 127 and this is also the max valid range for this Extra parameter.

ActivationMode argument


In most circustances you can ignore this argument.
It is a mask bit of SCANF_ mnemonic constants to inform you about how the trigger has been detected.


You can test the bits using the code:

          if (ActivationMode & enumSCANF.BUTTON_ONE_SHOT) {
                    // the trigger had One-shot button
          }

In above code we tested if the trigger had the [One Shot] button enabled.
You can see all different SCANF_ values with autoenumerate enumSCANF.
The SCANF values tells to you also some technical infos about from where this trigger has been taken.
When the trigger was in some sector of the level, there will be the SCANF_FLOOR_DATA flag.
If the trigger came from a TriggerGroup command of the script, there will be the SCANF_SCRIPT_TRIGGER flag.
If the trigger came from a AnimComand of some animation, there will be the SCANF_ANIM_COMMAND flag.
While when the trigger has been called from the code with PerformFlipEffect() function, there will be the SCANF_DIRECT_CALL flag.
Another interesting info is given by the SCANF_HEAVY flag. When this flag is present it means that the trigger has been enabled in HEAVY way, so not from Lara but from some enemy. The heavy is possible only when there is also the SCANF_FLOOR_DATA flag, of course.
note: When there is SCANF_HEAVY flag you can discover the index of moveable that enabled the trigger from this variable:
Trng.pGlobTomb4->ItemIndexEnabledTrigger

How to divide the code for each of our Flipeffects

Since there is only one callback that it will be used to manage all our flipeffect triggers, we have to check the value of FlipIndex argument to perform the right code in according with the current flipeffect.
Our cbFlipeffectMine() function is currently almost empty:

// this procedure will be called everytime a flipeffect of yours will be engaged
// you have to elaborate it and then return a TRET_.. value (most common is TRET_PERFORM_ONCE_AND_GO)
int cbFlipEffectMine(WORD FlipIndex, WORD Timer, WORD Extra, WORD ActivationMode)
{
          int RetValue;
          WORD TimerFull;

          RetValue = enumTRET.PERFORM_ONCE_AND_GO;
          // if the flip has no Extra paremeter you can handle a Timer value with values upto 32767
          // in this case you'll use the following TimerFull variable, where (with following code) we set a unique big number
          // pasting togheter the timer+extra arguments:
          TimerFull = Timer | (Extra << 8);

          switch (FlipIndex) {
                    // here type the "case Number:" for each flipeffect number. At end of the code you'll use the "break;" instruction to signal the code ending
          }

          return RetValue;
}

Inside of switch() statement, inside of its brackets, we'll type the "case Number:" for our new trigger.
Since it has 800 as value, we'all add this code:

          switch (FlipIndex) {
                    // here type the "case Number:" for each flipeffect number. At end of the code you'll use the "break;" instruction to signal the code ending
          case 800:
                    // Experiments. Perform <&>experiment passing to it the (E)Value
                    break;
          }

We added the line:

          case 800:

to signal the begin point of the code to perform when in above "switch() statement, the FlipIndex value will be "800"
We added also a comment row to remember quickly what was this flip 800:

                    // Experiments. Perform <&>experiment passing to it the (E)Value

We typed immediatly also the instruction to stop the code inside a "Switch() case" statements, to avoid to forget this important instruction.

                    break;

Now, all the code that we'll type between the "case 800:" row, and the following "break;" row, it will be performed ONLY when FlipIndex is = 800 and therefor, only when the flipeffect 800 has been activated.

Returned value from cbFlipEffectMine() callback

In code showed in above paragraph it has been said that the code for a specific flipeffect will be enclosed between the "case Number:" and "break;" instructions.
This is true in most circustances but sometimes you could end your code with a "return Value;" instruction.
While the "break;" instruction, skip other "case ..:" values and it will perform the code outside of brackets of current "switch()" statement, the "return ...;" instruction will quit immediatly from the current function.
In cbFlipEffectMine() function if you use the "break;" the next code to be performed it will be that you see after the ending closed bracket of "switch()" :

          // if there was the one-shot button enabled, return TRET_PERFORM_NEVER_MORE
          if (ActivationMode & enumSCANF.BUTTON_ONE_SHOT) RetValue= enumTRET.PERFORM_NEVER_MORE;
          return RetValue;

So, in this case the "break;" will perform anyway a "return" instruction but with the difference that if you use "break;" the value returned will be that in variable "RetValue"
At begin of the function it had been set a default value for "RetValue" variable:

          RetValue=TRET_PERFORM_ONCE_AND_GO;

This means that if you use a "break;" to complete your code, it will be performed the instruction "return RetValue;" and so the value TRET_PERFORM_ONCE_AND_GO will be returned.
In the case you wish return a different value, you have two chances:


Meaning of TRET returned values

TRET means Trigger RETurn.
The value you return it will be used from trng engine to discover when (or if) to call newly same trigger.
By default tomb engine will detect a trigger everytime Lara (or some enemy for heavy triggers) stands on that sector.
This means that the trigger could be performed 30 times for second until the activator object remains on that sector.
Above behavior it's not good for us, usually we wish perform only once the trigger, in spite Lara is yet on same sector, with only exception for condition trigger when the condition is yet false, since we wish continue to verify if the condition becomes true.

Note: Since the returned value affects the way to check the trigger on the floor sector, the matter is different when current trigger has been called from another source, like: triggergroup from script, animcommand from animation or with a direct call. In all above situation it will be ignored the returned value with the only exception of TRET_EXECUTE_ORIGINAL value.

TRET_PERFORM_ONCE_AND_GO constant

The name of this constant is not so clear but a more esplicative sentence it should have been too long.
The meaning is: "Perform only once the trigger, until activator object in on this sector, then, when it goes aways and then comes back newly on the sector, activate this trigger newly"
This setting is the most common for flipeffect and action trigger that have NOT the ONE-SHOT button.
It is for this reason that in default ending code we find the instruction:

          if (ActivationMode & enumSCANF.BUTTON_ONE_SHOT) RetValue= enumTRET.PERFORM_NEVER_MORE;

It works to transform a ONCE_AND_GO in a NEVER_MORE activation way only when it has been set the ONE-SHOT button in the trigger settings.

TRET_PERFORM_NEVER_MORE constant

How it's easy to understand, when you return the TRET_PERFORM_NEVER_MORE value, current trigger (on that sector) it will be never more executed.
This value should be returned only when there is ONE-SHOT button enabled in the trigger settings.

TRET_PERFORM_ALWAYS constant

It's more seldom returning this value, anyway we can imagine two situations where you could use this value:

  1. When you wish injury Lara until she stands on current sector. In this situation you wish that the trigger was continuosly performed and not only once. Same speech if you wish recharge lara in continuous way until she is over this recharging trigger.
  2. When you wish apply some physical effect on lara or enemy. For instance if you wish that this trigger worked like a dry sink: attracting the activator object in some diretion with soft movement, you'll have to perform continuosly the trigger until the activator stands on it.

TRET_EXECUTE_ORIGINAL constant

You cann't return this value from callback of your triggers. This value will be used only when you wish affect some trng trigger and your callback procedure could wish return this value to give the command to perform also trng original code in spite you had set a CBT_REPLACE callback type.
This topic will be explained better in help file about the callback for trng triggers.




How to add a new Action trigger

In this chapter it will be described only the differences respect the adding of a flipeffect.
The description for an action trigger will be typed inside of this section of TRG file:

<START_TRIGGERWHAT_11_T_H>
92:Enemy. Mesh. Flip (E)Mesh of <#>Moveable
<END>

While the section for its parameters will be typed after and outside of above section, in this way:

<START_ACTION_92_O_H>
#MOVEABLES#
<END>

<START_ACTION_92_E_H>
#REPEAT#Mesh=#0#63
<END>


The cbActionMine() callback

When an action trigger of yours it will be activated in game, it will be perfomed the code of cbActionMine() function:

// this procedure will be called everytime an action trigger of yours will be engaged
// you have to elaborate it and then return a TRET_.. value (most common is TRET_PERFORM_ONCE_AND_GO)
int cbActionMine(WORD ActionIndex, int ItemIndex, WORD Extra, WORD ActivationMode)
{
          int RetValue;
          
          RetValue=TRET_PERFORM_ONCE_AND_GO;

          switch (ActionIndex) {
                    // type here the code per your action trigger.
                    // add "case Number:" and complete the code with "break;" instruction
          }          
          // if there was the one-shot button enabled, return TRET_PERFORM_NEVER_MORE
          if (ActivationMode & enumSCANF.BUTTON_ONE_SHOT) RetValue= enumTRET.PERFORM_NEVER_MORE;
          return RetValue;
}


ActionIndex argument

ActionIndex is the number of action trigger that it has been just detected.
For instance, following above example ("92:Enemy. Mesh. Flip (E)Mesh of <#>Moveable"), it should have a value = 92.
ActionIndex variable is already present in cbActionMine() function, as parameter of "switch()" statement.
You should add a new "case NumberOfAction:" instruction, inside of the "switch()" and then complete the code for that action trigger, with the "break;" instruction.
Example:

          switch (ActionIndex) {
                    // type here the code per your action trigger.
                    // add "case Number:" and complete the code with "break;" instruction
          case 92:
                    // here type the code to manage the action trigger 92
                    // and at end, perform following instruction:
                    break;
          }


ItemIndex argument

ItemIndex variable will have the value of "(Object to trigger <#>)" field in Set Trigger Type window.
In most cases it will be an index of some Moveable Item.
It happens also in our example: "92:Enemy. Mesh. Flip (E)Mesh of <#>Moveable"
About index of moveables it's important to know that if current value is a moveable index it will be already in tomb format and not in NGLE format.
It could seem weird but the reality is that NGLE program, before saving .tom file (that, then, it will be moved in .tr4 file) it will convert NGLE index format to TOMB index format.
This means that when you receive ItemIndex value, it is already in native tomb format and for this reason you have not to convert it (from ngle to tomb format) and neither to use the NGLE_INDEX constant linked with it.
You can get immediatly the moveable structure pointed by that index in this way:

          Get(enumGET.ITEM, ItemIndex,0);
          GET.pItem->


Extra argument

Extra is the value of (E)xtra argument of the trigger.
For instance in our example "92:Enemy. Mesh. Flip (E)Mesh of <#>Moveable", it will be the value of "(E)Mesh".

ActivationMode argument

ActivationMode variable will have SCANF_ values about source of this action trigger. See ActivationMode argument paragraph about flipeffect.

Returned value from Action trigger callback

The cbActionMine() callback has to return a TRET_ value.
It works exactly like the Returned value from cbFlipEffectMine() callback




How to add a new Condition trigger

The condition triggers will be typed in "<START_TRIGGERTYPE_12_T_H>" section, in this way:

<START_TRIGGERTYPE_12_T_H>
21:Creature. Current animation of <#>creature is (E)animation (0-31)
<END>

The arguments used by condition trigger will be typed: in "<START_CONDITION_nn_O_H>" section for "<#>" argument, and into the "<START_CONDITION_nn_B_H>" section for "(E)" argument.
So the whole declaration of our example it will be:

<START_TRIGGERTYPE_12_T_H>
21:Creature. Current animation of <#>creature is (E)animation (0-31)
<END>
<START_CONDITION_21_O_H>
#MOVEABLES#
<END>
<START_CONDITION_21_B_H>
#REPEAT#Animation=#0#31
<END>


The cbConditionMine() callback

In plugin_trng.cpp you find the cbConditionMine() function/callback:

// this procedure will be called everytime a conditional trigger of yours will be engaged
// you have to elaborate it and then return a CTRET_.. value (most common is CTRET_ONLY_ONCE_ON_TRUE)
int cbConditionMine(WORD ConditionIndex, int ItemIndex, WORD Extra, WORD ActivationMode)
{
          int RetValue;

          RetValue=CTRET_ONLY_ONCE_ON_TRUE;

          switch (ConditionIndex){
                    // type here the code for your condition trigger, inserting the code in the section
                    // beginning with "case NumberOfAction:" and ending with row "break;"

          }
          return RetValue;

}


Important note about code of Condition Trigger

When you type the code to manage your condition trigger, you should take care when you call the GetFloor() function, or other functions that could call it, like CheckFloor(), CheckDirection() and many ENV conditions.
If you are going to call a function with GetFloor(), from your condition code , you'll have to use this instruction before calling that "GetFloor" function:

Trng.pGlobTomb4->BaseSalvaFloor.InsideCondition++;

While immediatly after have called (last) GetFloor function, you should to type this opposite instrunction:

Trng.pGlobTomb4->BaseSalvaFloor.InsideCondition--;


The description of this issue is a bit complicated...
The condition trigger will be parsed when the tomb raider function TestTriggers() it will be called.
But this parsing will be performed also when, inside of GetFloor() function, the tomb engine detects the height of the floor.

This happens because some dummy triggers may affect the collision geometry moving up the floor, like it happens for bridge objects.
In this situation it could born an endless loop:

This is bad...
You could have a crash for stack overflow, because one function calls itself in endless way.

To avoid this endless cycle, you have to increase the value of "InsideCondition" global variable, in this way it will be different than 0.
Now when trng engine should dispatch a new condition trigger, if it discovers that "InsideCondition" is > 0, it understands that there is already a condition trigger that is currently in progress, and for this reason it will skip the furhter performing of condition triggers, until the "InsideCondition" variable is different than 0.
Once you decreased the "InsideCondition" variable, it will come back to the value 0, and the condition triggers will be newly performed.

Note: Please take care to increase and reduce correctly the "InsideCondition" variable only once, otherwise all condition triggers will stop to work forever.


ConditionIndex argument

The valid range for ConditionIndex is 0 / 255.
You'll type the code for your condition trigger inside of the brackets of "switch(ConditionIndex)" statement, beginning with an instruction "case NumberOfAction:"
For instance in our example "21:Creature. Current animation of <#>creature is (E)animation (0-31)", it will be:


          switch (ConditionIndex){
                    // type here the code for your condition trigger, inserting the code in the section
                    // beginning with "case NumberOfAction:" and ending with row "break;"
          case 21:
                    // type here the code for condition 21
                    break;
          }

}


ItemIndex argument

ItemIndex variable will have the value of "(Object to trigger <#>)" field in Set Trigger Type window.
In most cases it will be an index of some Moveable Item.
It happens also in our example: "21:Creature. Current animation of <#>creature is (E)animation (0-31)"
About index of moveables it's important to know that if current value is a moveable index it will be already in tomb format and not in NGLE format.
It could seem weird but the reality is that NGLE program, before saving .tom file (that, then, it will be moved in .tr4 file) it will convert NGLE index format to TOMB index format.
This means that when you receive ItemIndex value, it is already in native tomb format and for this reason you have not to convert it (from ngle to tomb format) and neither to use the NGLE_INDEX constant linked with it.
You can get immediatly the moveable structure pointed by that index in this way:

          Get(enumGET.ITEM, ItemIndex,0);
          GET.pItem->


Extra argument

Extra is the value of (E)xtra argument of the trigger.
For instance in our example "21:Creature. Current animation of <#>creature is (E)animation (0-31)", it will be the value of "(E)Animation"
The Extra argument for condition trigger has a limited range: 0 / 31
When you create a condition trigger that requires an (e)xtra argument, you should take care that its max value was lower than 32, and then you have to add the CTRET_EXTRA_PARAM flag to returned value to inform trng engine that your trigger had used the activaction buttons of the trigger to host this extra value.

ActivationMode argument

ActivationMode variable will have SCANF_ values about the source of this condition trigger. See ActivationMode argument paragraph about flipeffect.

Returned values from cbConditionMine() callback. The CTRET_ flags

The cbConditionMine() callback has to return a CTRET_ value.
Please remember that this kind of value is different than those returned from callbacks for flipeffect and action triggers.
The CTRET (Condition Trigger RETurn) has different values and this kind of values are bit flags while the TRET_ values were absolute values.
The difference is that, while with TRET_.. value you set its value with "=" operator and check (read) its value with "==" operator, the CTRET_ values are bits, and you'll add one single bit using the "|" (or "+") operator, while you'll test its presence using the "&" (binary and) operator.
The CTRET_ values are a bit more compilcated to be set because you have to understand some technical issues hidden behind the condition triggers.

CTRET_EXTRA_PARAM flag

Everytime your condition trigger used an (E)xtra parameter you have to add (to returned value) the CTRET_EXTRA_PARAM flag.
The technical reason is that, to have a little extra parameter in condition trigger, trng uses the field used usually to store the activation buttons "[One-shot][5][4][3][2][1]" of the trigger, as a variable where to store this extra parameter.
This is a dirty trick, and to avoid problem in following execution, it's important, immedialty after having performed the condition trigger, that trng restored the button values to valid values.
To perform this operation trng engine has to know if that condition trigger used really an (E)xtra parameter.
It will be your cbConditionMine() function to inform it about this matter, adding to returned value the CTRET_EXTRA_PARAM flag
This means that, only when your condition has an (E)xtra parameter, you'll have to type also the instruction:

          RetValue |= enumCTRET.EXTRA_PARAM;

Above instruction add to value of RetValue variable, the CTRET_EXTRA_PARAM flag
Another way to get same result, is to quit your code with a "return" instruction where you add also the CTRET_EXTRA_PARAM flag.
For instance:

          return enumCTRET.EXTRA_PARAM | enumCTRET.PERFORM_ALWAYS | enumCTRET.ON_MOVEABLE;


CTRET_ON_MOVEABLE flag

You have to add this flag to returned value, everytime that your condition trigger used a moveable index as "(Object to trigger <#>)" field.
There is tehcnical reason for this.
If you omit to signal that your condition trigger used a moveable index, the tomb engine it will try to activate that moveable like it was a common "trigger object" trigger.
In spite it's not sure that this situation affect problems, it's better signal that you used a moveable index, in this way trng will skip this value in trigger parsing performed by tomb engine, avoding weird results.

For instance in our example "92:Enemy. Mesh. Flip (E)Mesh of <#>Moveable", we used own a moveable index, so the code to manage this condition trigger should add to returned value also the CTRET_ON_MOVEABLE flag.

CTRET_IS_TRUE flag

The main target of a condition trigger is to detect if some condition is true or false.
Once your code discovered if the condition is true or less, it will have to return a flag to inform trng egine about the result.
If the condition is true, you have to add the CTRET_IS_TRUE flag to returned value.

          return enumCTRET.IS_TRUE | enumCTRET.EXTRA_PARAM | enumCTRET.ONLY_ONCE_ON_TRUE | enumCTRET.ON_MOVEABLE;

Please, note that it doesn't exist a CTRET_IS_FALSE flag.
Simply if you omit to add the enumCTRET.IS_TRUE flag, this means that the condition will be false.

CTRET_PERFORM_ALWAYS flag

Adding this flag to retured value you inform trng engine that you wish that your condition trigger was always performed, indifferently if the condition was true or false.
This is NOT most common "perform" flag, anyway we can use it when you wish that, when the condition is true, the trigger was continuosly performed to add or remove HP value, continuosly, or to simulate some phisical effect.

CTRET_ONLY_ONCE_ON_TRUE flag

This is most common "perform" flag to return.
It means: until the condition is false, continue to perform the condition trigger (to discover if in whiletime it will become true), then, when the condition is true, perform only once the triggers placed after this condition trigger.
Anyway, in the case lara move outside of the sector and the comes newly over that sector, the condition will be newly verified continuosly until it becomes true.

CTRET_NEVER_MORE_ON_TRUE flag

This flag works alike the previous CTRET_ONLY_ONCE_ON_TRUE flag, but in this case, once the condition has been detected as "true", it will be never more performed, also if lara move out from sector and then comes inside newly.
Pratically you should used this flag when you wish use a "ONE-SHOT" button setting on your condition trigger.

CTRET_PERFORM_ONCE_AND_GO flag

This flag remember the TRET_PERFORM_ONCE_AND_GO flag, used by flipeffect and actions, but, while for those triggers, it was very common, for condition trigger it is a bit unusual.
With this flag, indeed, you perform the condition only once when lara enter in the sector, and then, indifferently if the condition is true or false, it will be no more performed until lara move outside of the sector to come inside newly.
The only reason to use this weird setting, it's when the condition to check is a bit complicated to be computed and it requires many cpu time and/or you are sure that, if the condition is true (or false) when lara enters in the sector, it's not possible that the result changed while lara is over the sector.
For instance if the condition is an item that she should have (in inventory), if that item is not in that sector, for sure she will be not able to change the result of condition until she is over that sector.

CTRET_EXECUTE_ORIGINAL flag

This flag is not used for the callback of your trigger but only for the callback for trng triggers.
In that case you can add this flag to retuned value, to inform trng that you wish that also the original trng code of that trigger was performed.