How to create dynamically Script Commands

          Introduction
Why it's better do not abuse of dynamic script commands
          How to handle circumspectly the dynamic script commands
Functions used to create script commands
          Arguments used to create script commands
                    The TestDynamic argument
          Unsupported param types and limitations
How the automatic deleting of script commands works
          Persistence of Script Commands
          The cbInitLevel() callback


Introduction

There are some functions which you can allocate some script commands like they had been written in script.dat file.
This chance it has been thought to support those triggers that require some PARAM_ argument from script.
Since you can call in direct way all trng triggers it was a pity having to give up to many triggers only because they need of some script command.

Why it's better do not abuse of dynamic script commands

Said that, it's important understand that it's not advisable using in intensive way this chance, for some reasons:


How to handle circumspectly the dynamic script commands

To avoid the issues described in previous paragraph, we should follow some rules:
  1. Everytime it is possible, you should create dynamically a script command only just a moment before you need of it, before calling the trigger requiring it.
    Just the trigger has been completed, you should (*)delete that script command.
    In this way the quantity of dynamic script command will never increase too much, since you create them and delete them in short time.

    (*) Note: in many circustances you can create the command setting a parameter to require its immediate erasing when that trigger has been performed. This chance it's very useful when you are not sure about when the trigger has been completed and you cann't delete the script command in advance while it is yet used from the trng feature.

  2. To fix the problem about saving game in the middle of a trng feature requiring a dynamic script command, you can create the script command in advance, before beginning the level, only once.
    In this case you have no need to delete it.
    Anyway this chance should be used only in seldom circustances to avoid the problem to finish the script resources allocating too many script command in permanent way.

  3. Another way to fix the problem about saving game, it's to disable the saving skill in game for some time, the same time required to the trng feature to be completed.
    See the DisableSaving() function description in Function Collection help file.

  4. You should look the log messages from debug version, when you are working with dynamic script commands, to verify that, all script commands created in run-time (really dynamic) was also deleted immediatly after their usage.


Functions used to create script commands

You cann't create any script command.
Only some script commands could be created dynamically: those more used by trng triggers.
The functions to create script commands are low level functions and for this reason you have to call them using the Service() function.
With Service() function you call directly a function located in tomb_nextgeneration library.
First argument of Service() function is a SRV_ constant, to specify the kind of function you mean call.
While following arguments will the arguments to pass to that function.

You can use the autoenumerate enumSRV to locate the available SRV values.


In above image you can see the service to create commands: are all in capital letters and begin with SRV_CREATE....

Arguments used to create script commands

After the SRV_... constant, other arguments should be almost the same you use in that "real" script command but with some exception...


The TestDynamic argument
Almost all services to create script commands require as first argument a value like "true" or "false".
It will be the value that you supply for "TestDynamic" argument.
All commands requires TestDynamic as first argument, with the exception for following script commands:

Animation=
AnimationSlot=
Parameters=PARAM_BIG_NUMBERS

The reason is that above script commands have no ID to identify them.
Anyway in above cases the first argument will be the same of corresponding script command and not TestDynamic.
All others services require TestDynamic (true or false) as first argument.

When you set "true" you tell to trng: "When the trigger (that I'm going to call) it has been completed, remove yourself this script command, please".
This means that, with "true", the management of deleting for script commands it will happen automatically.

This is comfortable but there are seldom circustances where you would like set "false" it, to have a better control about next deleting.
Indeed, when you set "false", it will be you to have to call another service to delete that script command, and (in your code) you'll do that, only when you are sure that script command is no more used.
The services to delete a script command are the following:

SRV_DeleteParamCommand
SRV_DeleteTriggerGroup
SRV_DeleteColorRgb
SRV_DeleteMultEnvCondition
SRV_DeleteTestPosition
SRV_DeleteAddEffect

All above services have as only one argument: the Id of script to delete.
It's different only the SRV_DeleteParamCommand service, that requires as first argument the PARAM_ constant of that parameters= command and then the ID of script command to delete.

Service to manage dynamic Script Commands

All service to create script command don't require the ID, like it happens when you type them in the script, because the ID will be returned by the function to choose an ID different by all other for that kind of script command.
Service Arguments Description
SRV_CREATE_ADDEFFECT TestDynamic, EffectType (ADD_), FlagsEffect (FADD_), JointType (JOINT_), DispX, DispY, DispZ, DurateEmit, DuratePause, Extra param array, END_LIST The values to type as arguments, after the TestDynamic argument, are the same you type in AddEffect= script command, but skipping the Id argument because it will be returned by current service.
You have to type the END_LIST costant at end of the arguments to signal the end of the argument list
SRV_CREATE_ANIMATION AnimIndex, KEY1_ , KEY2_ , FAN_ flags, ENV_ Environment, Distance for Env, Extra, StateId (STATE_...) or (-)AnimationIndex array (...), END_LIST This service don't want the TestDynamic argument and neither it returns an ID (Animation= commands have no id)
The arguments are the same you type in Animation script command, with only exception that you have to terminate the sequence of arguments with the mnemonic constant END_LIST, to signal the end of optional array with stateids or animation numbers.
Note: it's not possible delete an Animation command once you create it.
For this reason you should place the code to create the animation command in some procedure where it will be performed only once for level.
SRV_CREATE_ANIMATIONSLOT Slot, ActionType (AXT_...), AnimIndex, Key1, Key2, FAN_ flags, ENV_ Environment, Distance For Env_, Extra, StateId (STATE_...) or (-)AnimationIndex array (...), END_LIST This service don't want the TestDynamic argument and neither it returns an ID (AnimationSlot= commands have no id)
The arguments are the same you type in AnimationSlot script command, with only exception that you have to terminate the sequence of arguments with the mnemonic constant END_LIST, to signal the end of optional array with stateids or animation numbers.
Note: it's not possible delete an AnimationSlot command once you create it.
For this reason you should place the code to create the animation command in some procedure where it will be performed only once for level.
SRV_CREATE_COLOR_RGB_COMMAND TestDynamic, Red, Green, Blue The values to type as arguments, after the TestDynamic argument, are the same you type in ColorRGB= script command, but skipping the Id argument because it will be returned by current service.
SRV_CREATE_MULTENVCONDITION TestDynamic, ENV_ condition, DistanceForEnv, Extra field, array of tripled of triple values {ENV_ Condition, DistanceForEnv, Extra field}, END_LIST The values to type as arguments, after the TestDynamic argument, are the same you type in MultEnvCondition= script command, but skipping the Id argument because it will be returned by current service.

You have to type the END_LIST costant at end of the arguments to signal the end of the argument list
SRV_CREATE_PARAM_COMMAND TestDynamic, PARAM_ type, arguments of given PARAM_ command, END_LIST The values to type as arguments, after the TestDynamic argument, are the same you type in Parameters= script command, but skipping the Id argument because it will be returned by current service.

Note: all PARAM commands require to complete the argument list with the END_LIST value
SRV_CREATE_TESTPOSITION TestDynamic, Flags (TPOS_...), Slot Moveable, XDistanceMin, XDistanceMax, YDistanceMin, YDistanceMax, ZDistanceMin, ZDistanceMax, HOrientDiffMin, HOrientDiffMax, VOrientDiffMin, VOrientDiffMax, ROrientDiffMin, ROrientDiffMax The values to type as arguments, after the TestDynamic argument, are the same you type in TestPosition= script command, but skipping the Id argument because it will be returned by current service.
SRV_CREATE_TRIGGERGROUP TestDynamic, Array with groups of three values of exported triggers, completed with END_LIST constant The values to type as arguments, after the TestDynamic argument, are the same you get from [Export Script Trigger] button of Set Trigger Type window of NGLE program.
You have to type the END_LIST costant at end of the arguments to signal the end of the argument list
The service will return the ID for the triggergroup command, or -1 if there is an error.
SRV_DeleteAddEffect IdOfAddEffect Delete the AddEffect command you had created with SRV_CREATE_ADDEFFECT service
SRV_DeleteColorRgb IdColorRgb Delete the ColorRgb command you had created with SRV_CREATE_COLOR_RGB_COMMAND service
SRV_DeleteMultEnvCondition IdOfMultEnvCond Delete the MultEnvCondition command you had created with SRV_CREATE_MULTENVCONDITION service
SRV_DeleteParamCommand PARAM_ type, IdOfParameterCommand Delete the parameter command you had created with SRV_CREATE_PARAM_COMMAND service.
The PARAM_ Type will be the same you used to create the command, and the IdOfParamCommand will be the value you got from SRV_CREATE_PARAM_COMMAND service when you created that command.
SRV_DeleteTestPosition IdOfTestPosition Delete the TestPosition command you had created with SRV_CREATE_TESTPOSITION service
SRV_DeleteTriggerGroup IdTriggerGroup Delete the triggergroup command you had created with SRV_CREATE_TRIGGERGROUP service


Unsupported param types and limitations

With the SRV_CREATE_PARAM_COMMAND service you can create a parameters= script command, choosing the PARAM_ type as first argument.
Anyway, there is some limitation:
You cann't create a Parameters=PARAM_WTEXT because it's not currently supported.
While, about the PARAM_ACTOR_SPEECH parameter, you can create it but with the limitation that you are not able to create dynamically new strings. This means that you cann't use the SPC_TEXT commands with dinamic strings, but only with "real" strings built in script.dat/english.dat files.

How the automatic deleting of script commands works

When you create a script command, setting TestDynamic as "true", you give to trng code the power to manage itself the deleting of that script command.
Trng it will use an plane method to do this: when a trigger using that script command, it has been completed, trng code will check if the script commands used from that trigger had been created with TestDynamic=true. If it's true, trng will delete immediatly that script commands.
Above method works fine in many situations but it could fail sometimes...
For instance, if you create some ColorRgb commands with TestDynamic=true, and then copy the IDs you got in other new script command, when one trigger used that script command, it will be deleted the main script command (for instance the Parameters=PARAM_SHOW_SPRITE) but also the the ColorRgb command whom Id you set in main script command.
This means that at end of that trigger all above script commands have been deleted.
It seems ok, but what's happen if you had created other script commands using same ID of colorrgb you set also in Parameters=PARAM_SHOW_SPRITE command?
It will happen that, at first execution (and usage) of that ColorRgb it will be deleted, and when you'll try to peform a second trigger using the same ColorRgb ID, it will fail because now that colorrgb is missing, since it had jst been deleted after the first trigger.
Above situation explains because in some circustance we prefer set TestDynamic as "false", to be sure that no script commands will be deleted itself, and then we'll delete them in explicit way usign some delete service for them.
The choice about using one or other method depends by the kind of script commands you create and the way you mean use them.
Pratically, if you wish using a single trigger with a given target, you can use TestDynamic = true and let to trng the target to delete the script commands.
While when you create a long list of script commands, where some of them (like the ColorRgb in above example) will be used as argument for other script commands, in this situation, probably, you prefer set TestDynamic =false, to avoid that some secondary script command, used twice or more, was deleted itself before to be used newly.

When you choose to set TestDynamic as false, you have the problem to know about "when" you can delete some script command, because it's not always so sure this matter.
Some triggers perform immediately their target and in this case you can work in this way:

Note: really in above situation you could use also the automatic deleting in following way:

And trng code will delete the script command for you, at end of trigger execution.

But what could happen when the trigger requires more time to be completed?
This happens when you use a trigger to begin a progressive action like moving an item from two positions in some time, with a smooth movement.
The trigger you call will be performed immediatly but the progressive action will go on for many frames, to complete the movement of that item.
In this situation you cann't delete the script command used by that progressive action, because it will be required for all durate of that action.
In this case it's more comfortable using TestDynamic as true, because only trng code will know the correct moment when the action has been completed and only in that moment it will delete the script command linked with that action.
To complicate the speech there is also another question: how to know when some trigger requires a script command for some time and when it requires that script command only in immediate way?
I'll try to explain this matter in following table, where you can see for any script command its persistence.





Persistence of Script Commands

Persistence of script commands in trng triggers

Script Command Persistence Description
AddEffect Yes Until the effect is added to some moveable, the code requires to have yet the AddEffect script command.
For this reason it's necessary delete the command only at end of the effect on that item
ColorRGB Mix Usually ColorRgb IDs will be used as argument for other script commands.
To discover the persistence of ColorRgb command you have to check the main script command where these ColorRbg commands are used.
MultEnvCondition No Since the MultEnvCondition will be performed in a single frame to detect if in that moment the sequence of conditions is true or false, immediatly after the call to condition trigger, the command is no more used.
Anyway this speech should be different when the MultEnvCondition will be used by some Animation or AnimationSlot command. In this case, in fact, the animation commands work forever and in same way also the linked MultEnvCondition commands.
Parameters=PARAM_ACTOR_SPEECH Yes Only when whole speech sequence has been completed it will be possible delete the PARAM_ACTOR_SPEECH command
Parameters=PARAM_BIG_NUMBERS No Once you called the trigger requiring that big number parameter, that value from PARAM_BIG_NUMBERS script command will be no more used.

Note: warning because the PARAM_BIG_NUMBERS works in different way respect other script commands...
There is no ID for PARAM_BIG_NUMBERS command, and neither the chance to use automatic deleting.
Only you can delete it after having used it, and to delete it you should supply as argument not the ID (that doesn't exist) but the same big number value you set when you created the PARAM_BIG_NUMBERS command.
Another difference is that when you create PARAM_BIG_NUMBERS command you, really, add simply to the PARAM_BIG_NUMBERS sequence, another value, but this is not a new script command but only a new value added to a persistent, shared, PARAM_BIG_NUMBERS command.

Note: the deleting of last number you added, it will work only if you had added only one big number.
Differently, if you had added in some moment two or more big numbers, you should call many time the SRV_DeleteParamCommand, PARAM_BIG_NUMBERS service, in opposite order respect to the number sequence.
Example:
Service(enumSRV.CREATE_PARAM_COMMAND, PARAM_BIG_NUMBERS, 510, 1024, 330);
With above service we have added to big numbers, our big numbers: 510, 1024 and 330
When we wish delete all above numbers, we should call three times the SRV_DeleteParamCommand service, passing as "id" (but it's not an ID) always the last number of our sequence.
So, we'll use following code to delete above our big numbers:

Service(enumSRV.DeleteParamCommand, PARAM_BIG_NUMBERS, 330);
Service(enumSRV.DeleteParamCommand, PARAM_BIG_NUMBERS, 1024);
Service(enumSRV.DeleteParamCommand, PARAM_BIG_NUMBERS, 510);

Pratically everytime we had supplied as argument the value that, in that moment, was the last of the sequence yet present.
This is only one way to delete a big number value we had added.
Parameters=PARAM_CIRCLE No Currently the PARAM_CIRCLE will be used only for condition triggers.
Once the condition has been performed the PARAM_CIRCLE command can be deleted.
Parameters=PARAM_COLOR_ITEM No Also when there is a change of the color for some time, the data from script command will be read and copied to progressive action struture, this means that the script command could be deleted immediatly after the trigger call.
Parameters=PARAM_LIGHTNING Yes Until the lightning will be drawn the PARAM_LIGHTNING command will be required.
Parameters=PARAM_MOVE_ITEM Yes The progressive action that handles the movement will require the presence of PARAM_MOVE_ITEM script command until at end of the movement
Parameters=PARAM_PRINT_TEXT Yes Until the text will be displayed on the screen the progressive action requires the presence of PARAM_PRINT_TEXT script command.
Parameters=PARAM_QUADRILATERAL No Currently the PARAM_QUADRILATERAL will be used only for condition triggers.
Once the condition has been performed the PARAM_QUADRILATERAL command can be deleted.
Parameters=PARAM_ROTATE_ITEM No Currently the trng code read the data in PARAM_ROTATE_ITEM command and copy these data in progressive action structure.
For this reason it's not necessary that the PARAM_ROTATE_ITEM command existed for whole time of rotating, it could be deleted immediatly after the call to the trigger.
Parameters=PARAM_SCALE_ITEM Mix When the scaling requires some time the linked PARAM_SCALE_ITEM has to be available for progressive action, while when the there is the FSCA_IMMEDIATE flag, the PARAM_SCALE_ITEM command will be ignored immediatly after the trigger call.
Parameters=PARAM_SET_CAMERA Yes Until there is the change of camera settings the progressive action requires the presence of PARAM_SET_CAMERA command
Parameters=PARAM_SHOW_SPRITE Yes For all time of sprite drawing, the PARAM_SHOW_SPRITE command has to be present.
Parameters=PARAM_SWAP_ANIMATIONS Yes In spite the swapping of animation works in immediate way, trng requires to get the presence of PARAM_SWAP_ANIMATIONS command to restore in future the original animation numbers.
For this particular working mode, it's not possible delete in automatic way the PARAM_SWAP_ANIMATIONS commands and it's neither advisable to delete it in direct way using the SRV_DeleteParamCommand service
Parameters=PARAM_TRIANGLE No Currently the PARAM_TRIANGLE will be used only for condition triggers.
Once the condition has been performed the PARAM_TRIANGLE command can be deleted.
TestPosition Mix The persistence of testpositon command changes in according with its settings.
When testposition command it has been set with TPOS_FAST_ALIGNMENT flag or called from an Animation command with FAN_ALIGN_TO_ENV_POS flag, it will be used for some frame.
Anyway, when it is a condition for animation command it should be anyway with a total persistence, while used as condition it will have no persistance.
TriggerGroup Mix Usually the TriggerGroup script command it will be used as secondary argument for some main script command.
In this situation you have to check the persistence of that main script command.
In the case you call it in direct way, it should have no persistence, with the exception if you use the flipeffect 373 (TriggerGroup. Perform <&>TriggerGroup from script.dat (continue performing to stop with f192 trigger)



The cbInitLevel() callback

Some script commands, like Animation= and AnimationSlot commands, should be created always in the code of cbInitLevel() function.
The reason is that these two commands require to be always present (you cann't delete them once you created them) and the body of cbInitLevel() function is ideal for this target since this function will be called only once for each level, always at starting of the level, also when the game has been loaded from savegame.
In this way, also if the game will be saved and reloaded from savegame these commands (created from cbInitLevel() function) will be always present.
If for animation= and animationslot= commands it's necessary create them from cbInitLevel() function, you could using also this point to create other script commands.
If you choose to place here the code for new script commands, you have to remember to create these commands always with TestDynamic as "false".
The only problem with this method is that already explained in Why it's better do not abuse of dynamic script commands chapter: all commands you type here as "false" for testdynamic will be always present and they will consume many script resources.

Anyway in your projecting you should think if these script commands are useful for each level or only when there is some specific item in the level.
About this speech, remember that when the control go to cbInitLevel() function, all moveables, statics and level stuf have been already loaded.
So you could verify if the script commands that your code uses for some new moveable or skills, it is really necessary in current level.
For instance, if we use a Parameters=PARAM_LIGHTNING to shoot a lightning from Star Wars Robot, you could verify if in current level it's really present one SW Robot, because if it is missing is futile allocate that script command.
Another situation, is when you are creating a plugin in hardcoded mode, i.e. built to work only with your specific adventure.
In this situation it could be useful also checking what level (number) is that, we are playing.
For instance if we know that some special feature (requiring some script command) has to work only in level 6 (the 0 is title), you could type in cbInitLevel() function a code like this:

void cbInitLevel(void)
{
          // here you can initialize specific items of currnet level.
          // it will be called only once for level, when all items has been already initialized
          // and just a moment before entering in main game cycle.


          Get(enumGET.GAME_INFO,0,0);
          if (GET.GameInfo.LevelIndex == 6) {
                    // now create script commands for level 6:

And the creation of script commands you'll type now, will be added only when it is working level 6 and not others.