Tomb Raider Forums  

Go Back   Tomb Raider Forums > Tomb Raider Level Editor and Modding > Tomb Raider Level Editor > Tutorials and Resources

Closed Thread
 
Thread Tools
Old 03-11-17, 21:56   #31
AkyV
Moderator
 
Joined: Dec 2011
Posts: 4,881
Default

30. Dynamical Script commands

AddEffect, Animation, ColorRGB, TriggerGroup etc. – there are a few Script command like this, which also work in the code. These code Script commands are called “dynamical” Script commands.
Mostly you don’t have any serious reason to use them. I mean, for example, if you want a ColorRGB command in the code, then use GET_COLOR_RGB instead.
But sometimes it is complicated. I mean, for example, it is not easy to make a code for TestPosition properties, from scratch. It is much easier if you use a TestPosition dynamical command.

But, remember, Script commands are for the level builders first of all. You, the plugin maker should avoid them in the code, any time you can.

Once again, perhaps it is not clear for you:
- If you make a code for a Script command (like PARAM_SPEED_ROLLINGBALL above), that is not “dynamical”. That is a command you make for the level builder.
- Dynamical commands are already made, you can find them in trng.dll, the level builder and you both can use them. (Just like the trng.dll triggers that could be used both by the level builder and – in the form of “exported function” – by the plugin maker. The difference is dynamical commands are not recommended for the plugin maker.)

Here is a detailed tutorial about dynamical Script commands. You should read it only if you need commands like that.
AkyV is online now  
Old 03-11-17, 21:58   #32
AkyV
Moderator
 
Joined: Dec 2011
Posts: 4,881
Default

31. Customize Script commands

Customize Script commands set a customized value for a parameter. The validity of this value is the whole game or the whole level. That is why you call these commands in the title, or in the moment when that level starts. (So a good place for the code eg. is cbInitLevel callback.)
You can make your own Customize Script commands. The structure is very alike to a Parameters command:

1. You need a description for this CUST constant (and the constants used for this CUST) in SCRIPT file.
2. You need “define” entries for these constants in Constants_mine.h . (Don’t forget: CUST and PARAM constants have a different ID range.)
3. You need to create the code – for example:

Code:
                    if (Get(enumGET.MY_CUSTOMIZE_COMMAND, CUST_LARA_SIZE, -1) == true) {
                          Get (enumGET.MY_CUSTOMIZE_COMMAND, CUST_LARA_SIZE, -1);
  
                          MyData.LaraWidth = GET.pCust->pVetArg[0];
                          MyData.LaraHeight = GET.pCust->pVetArg[1];
                          MyData.LaraTransparency = GET.pCust->pVetArg[2];
  
                          some code;
                    }
You need to realize:
- That odd “if” condition signals your CUST this time the way like this: “if CUST is exist, then execute the CUST”.
- GET.pCust this time, not GET.pParam!
- No command ID this time for the first field (as you can also see that in that -1), so even that field (with [0]) is used like the other fields.
AkyV is online now  
Old 03-11-17, 21:59   #33
AkyV
Moderator
 
Joined: Dec 2011
Posts: 4,881
Default

32. OCB codes

The descriptions for the custom OCB codes can be made in an OCB file.

Like TRG or SCRIPT files, OCB is also a special TXT file.
Like SCRIPT files, it also needs to be placed in the main folder of NG Center. (With “the usual file name” for this plugin. Which is Plugin_trng now.)
Like SCRIPT files, it also can be edited even with Trng Patcher – but unlike SCRIPT, OCB files can be edited without any “Expand” or “Compact”. All you need to separate the different help files in NG Center is place a #START_TAG# where a new help file starts. - For example:

#START_TAG#=_NEW Interact items
I mean, items that Lara can interact with (kickdoor, switch etc.)
OCB 1111: a technical value, you don't need to add manually (see: PARAM_ITEM_INTERACTION).

#START_TAG#=_NEW Camera Target
When you use a TARGET trigger without a CAMERA trigger (which I called a "standalone" TARGET trigger), "only" to draw Lara's attention to the target object, then you can use OCB numbers to fix some bugs.

OCB 1: standalone TARGET triggers always work as if they had the "One Shot" button pushed. This OCB will remove this, you will be able to activate the TARGET trigger each time when Lara goes into the trigger zone.
OCB 2: if you don't use OCB 1, then it definitely seems buggy that if you save/load the game, then the TARGET trigger (already used) will become useable again (once more) automatically. This OCB will disable this, loading a game won't enable the trigger.

The codes for the new OCB’s are easy. For example – a cbCycleBegin code:

Code:
        {
        int i;
        int Index;
  
        Find (enumFIND.ITEM, SLOT_CAMERA_TARGET, -1, 1, -1, NULL);
              for (i=0 ; i < FIND.TotItems ; i++) {
                    Index = FIND.VetItems[i];
                    Get (enumGET.ITEM, Index, 0);
                    GET.pItem->FlagsMain = 32;
              }
        }
The game will continuously search for all the CAMERA_TARGET objects with OCB 1. If it can find an object like that, then ItemFlag 32 (=”still inactive item”) will be forced for that object. (Because OCB1 was set, as I said just above, to remove that unwanted “One Shot”. Which means you can activate it again and again only if it is detected as inactive.)

Attention!
If there are more than one plugin installed and all of the makers of those plugins added the same OCB for the same object, then that is a bad issue, if the level builder wants to use both features.
So plugin makers should discuss this cleverly between each other.
(“Technical OCB”, as I tell it about 1111 just above, is a value which is needed in the code to separate THESE objects from THOSE objects, but not important for the level builders. I added that to that OCB file only because now other plugin makers will see that that OCB is already “reserved”.)

Last edited by AkyV; 12-11-17 at 11:44.
AkyV is online now  
Old 03-11-17, 22:00   #34
AkyV
Moderator
 
Joined: Dec 2011
Posts: 4,881
Default

33. GlobalTrigger constants

You can add custom GT constants for the GlobalTrigger command. First, just like in the case of PARAM and CUST, you need to add this constant to the SCRIPT file and Constants_mine.h.

You need to call DetectedGlobalTriggerEvent function in the code, to define the contents of your GT constant:

bool DetectedGlobalTriggerEvent(int GT_Event, int Parameter, bool TestIgnoreParameter)

GlobalTriggers are continuous events, so DetectedGlobalTriggerEvent should be executed under continuous circumstances!

For example:

Trigger code:
To resize Lara between size 2 and 5, the size value is stored in MyData.Size variable.

cbCycleBegin code:
DetectedGlobalTriggerEvent (GT_LARA_SIZE, MyData.Size, false);

In the Script:
GlobalTrigger= 1, IGNORE, GT_LARA_SIZE, 4, IGNORE, 1, IGNORE
TriggerGroup = 1, ...

The first value of DetectedGlobalTriggerEvent (GT_LARA_SIZE) is the name of that GT.
The second value (MyData.Size) is the parameter value that you can ask in the GlobalTrigger command having GT_LARA_SIZE. (Having -1 here now means it is without a parameter.)
The third value (true or false) indicates that there is (false) or is not (true) a parameter for that GT:

So TriggerGroup1 will be executed at each tick frame, while Lara’s size is 4.
So if you have a GT_LARA_SIZE GlobalTrigger now in the Script, enabled, and the value (size) you typed in the parameter field of the GlobalTrigger is the actual value of MyData.Size variable (“size”), then the GlobalTrigger is true.

Attention!
Just like in the case of OCB codes, GT constant ID codes (in SCRIPT and Constants_mine.h) also can have coincidences. The valid GT constant range is between 1 and 32767. Try to choose an ID for your constant which are not used by other plugin makers.
AkyV is online now  
Old 03-11-17, 22:01   #35
AkyV
Moderator
 
Joined: Dec 2011
Posts: 4,881
Default

34. Using textual variables

The easiest way to print text on the screen via the code is if you export trng.dll Text Flipeffects as functions.
Yes, that means you will not be able to print texts in the code without using TRNG textual/numerical variables and/or without the strings of NG Center, after reading this tutorial. But I don’t think it is really problem for a beginner. (Later I will tell more textual things in a single tutorial.)
AkyV is online now  
Old 03-11-17, 22:05   #36
AkyV
Moderator
 
Joined: Dec 2011
Posts: 4,881
Default

35. How to control default timers?

Let’s see for example the Squishy_Block2 trap. Activating the block, the level starts shaking, and a frame counter starts from 0. Reaching Value 60 (=2 seconds), the shaking and the counter stops (and remains 60), and the block falls down.
Our goal now is to force either a bigger or a smaller maximum shaking time for the trap, controlled in Custom A (Reserved_34) field.

The trigger code is this (maximum only for one object at the same time now):

Code:
        Get (enumGET.MY_PARAMETER_COMMAND, PARAM_SQUISHY_BLOCK2, TimerFull);
  
        MyData.Save.Local.SBIndex = GET.pParam->pVetArg[1];
        MyData.Save.Local.SBFall = GET.pParam->pVetArg[2];
  
        Get (enumGET.ITEM, MyData.Save.Local.SBIndex + NGLE_INDEX, 0);
  
        if (GET.pItem->SlotID == SLOT_SQUISHY_BLOCK2) {
              PerformActionTrigger(NULL, 43, MyData.Save.Local.SBIndex | NGLE_INDEX, 0);
              MyData.Save.Local.SBFlag = 1;
              MyData.Save.Local.SBTime = 0;
        }
GET.pParam->pVetArg[1] is the object (NGLE) index, GET.pParam->pVetArg[2] is the required time (in seconds).
The purpose of that PerformActionTrigger is to activate the block (if it is not already active), because it is meaningless (and effectless) to force any time for an inactive block.

And this is the cbCycleBegin code:

Code:
        if (MyData.Save.Local.SBFlag == 1) {
              MyData.Save.Local.SBTime++;
  
              Get (enumGET.ITEM, MyData.Save.Local.SBIndex + NGLE_INDEX, 0);
  
              if (MyData.Save.Local.SBTime >= MyData.Save.Local.SBFall * 30) {
                    GET.pItem->Reserved_34 = 60;
                    MyData.Save.Local.SBTime = 0;
                    MyData.Save.Local.SBFlag = 0;
              }
              if (GET.pItem->Reserved_34 == 59 && MyData.Save.Local.SBTime < MyData.Save.Local.SBFall * 30) {
                    GET.pItem->Reserved_34 = 0;
              }
        }
The trigger set Value 1 for SBFlag, so this cbCycleBegin code will be executed.
First, a custom frame timer will start from 0 in SBTime variable – see SBTime++. (See the trigger code for always starting it from 0 value.)
“SBFall * 30” is the required time, in tick frames. For example, if you wanted 5 seconds to shake, then it is 150 now. There will be checked if this value has been exceeded or not. If it has, then the procedure stops (Flag=0), also turning the custom frame timer into 0 (not to waste any memory unnecessarily) – and, what really matters, it turns Reserved_34 to 60, which means “fall down”.
Besides, there will also be checked if Reserved_34 is 59. If it is, then Reserved_34 won’t be let reach 60 to fall down, but it will be turned back into 0, still starting running again to 60 automatically.

Code:
  Reserved_34       SBTime
  0                 0
  1                 1
  ...               ...
  58                58
  59 => 0           59
  1                 60
  2                 61
  ...               ...
  58                117
  59 => 0           118
  1                 119
  2                 120
  ...               ...
  30                148
  31                149
  32 => 60          150 (=SBFall * 30) => 0
Note:
“MyData.Save.Local.SBTime < MyData.Save.Local.SBFall * 30” condition (so “if the custom timer is less than the limit of 150 frames”) is necessary only in the special case when somehow the custom timer expires exactly in the moment when Reserved_34 is 59. I mean, if it is not true, then not that second one, but the first condition will work for Frame59, so Reserved_34 won’t be turned to 0 but 60.

Countdown timers:

Let’s see for example the Enemy Jeep. If the jeep is parked at an AI_FOLLOW nullmesh, then a frame counter starts from 149. If it reaches 0 (i.e. in 5 seconds) then the jeep shoots a grenade, and the counter starts again from 149.
Our goal now is to force either a bigger or a smaller value for the time between shooting two grenades, controlled in Custom C (Reserved_38) field. This value is 12 seconds in our example.

The trigger code is this (maximum only for one object at the same time – which is logical now, maximum one enemy jeep is useable per level):

Code:
        Get (enumGET.MY_PARAMETER_COMMAND, PARAM_ENEMY_JEEP, TimerFull);
  
        MyData.Save.Local.EJeepGrenadeIndex = GET.pParam->pVetArg[1];
        MyData.Save.Local.EnemyJeepGrenadeTime = GET.pParam->pVetArg[2];
  
        MyData.Save.Local.EnemyJeepGrenadeFlag = 1;
GET.pParam->pVetArg[1] is the object (NGLE) index, GET.pParam->pVetArg[2] is the required time (in seconds).

And this is the cbCycleBegin code:

Code:
        if (MyData.Save.Local.EnemyJeepGrenadeFlag == 1) {
              Get (enumGET.ITEM, MyData.Save.Local.EJeepGrenadeIndex + NGLE_INDEX, 0);
  
              if (GET.pItem->SlotID == SLOT_ENEMY_JEEP) {
                    if (GET.pItem->Reserved_38 == 0) {
                          MyData.Save.Local.EJeepGrenadeNull = 1;
                    }
                    if (GET.pItem->Reserved_38 == 149 && MyData.Save.Local.EJeepGrenadeNull == 1) {
                          GET.pItem->Reserved_38 = MyData.Save.Local.EnemyJeepGrenadeTime * 30;
                          MyData.Save.Local.EJeepGrenadeNull = 0;
                    }
              }
        }
The trigger set Value 1 for EnemyJeepGrenadeFlag, so this cbCycleBegin code will be executed.
There is another flag in the cbCycleBegin code, called EJeepGrenadeNull. Let’s suppose the timer (Reserved_38) is just at 40 frames when you force 12 seconds for the time. If the timer reaches 0, the grenade is being shot. (So, now, the first grenade will be shot everyway using the default time, reaching 0, before starting using the customized time.)
Null flag goes 1 now so if the timer turns to 149 now, then 360 (=12*30) tick frames will be forced now for the timer, which continue decreasing from frame to frame, till 0. (Meanwhile, if it reaches 149, it won’t be turned to 360, because Null flag is 0 now.)
When it reaches 0, then the jeep shoots the grenade, the timer turns to 149 again, 360 will be forced again, it goes to 0 again etc.

40
39

2
1
0 (Null flag = 0 => 1)
149=>360 (Null flag = 1 => 0)
359
358

151
150
149 (Null flag = 0)
148
147

2
1
0 (Null flag = 0 => 1)
149=>360 (Null flag = 1 => 0)
359
etc.

Note:
You need a code with turning the main Flag to 0, if you want to restore the default timer.


END OF TUTORIAL

Last edited by AkyV; 03-12-17 at 23:10.
AkyV is online now  
Closed Thread

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off



All times are GMT. The time now is 10:40.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.
Tomb Raider Forums is not owned or operated by CDE Entertainment Ltd.
Lara Croft and Tomb Raider are trademarks of CDE Entertainment Ltd.