Level Endings - Classic Style
by AkyV
Download a project file illustrating this tutorial

I think everybody who played Tomb Raider before TR4 knows what 'classic level-ending' means:

So, the game freezes, the list of statistics pops up and we can hear a typical audio file. And, after that, the game loads the next level, of course.

And we can do the same in NGLE:

It's easy:

1. Don't use FINISH trigger where you want the end of the level. Instead of that, place a FLIPEFFECT there that activates a TriggerGroup - for example, TriggerGroup#1:

; Set Trigger Type - FLIPEFFECT 118
; Exporting: TRIGGER(257:0) for FLIPEFFECT(118)
; <#> : TriggerGroup. Perform <&>TriggerGroup from script.dat in (E)way
; <&> : TriggerGroup= 1
; (E) : Single performing (to use when in TriggerGroup there are only commands)
; Values to add in script command: $2000, 118, $101

2. Place the entry of that TriggerGroup in the Script:

TriggerGroup= 1, $2000, 130, $0, $2000, 129, $2C, $2000, 223, $0, $2000, 82, $2

; Set Trigger Type - FLIPEFFECT 130
; Exporting: TRIGGER(0:0) for FLIPEFFECT(130)
; <#> : Sound. (CD) Stop CD track of <&>Channel
; <&> : Channel 1 (Background track)
; (E) : 
; Values to add in script command: $2000, 130, $0

; Set Trigger Type - FLIPEFFECT 129
; Exporting: TRIGGER(44:0) for FLIPEFFECT(129)
; <#> : Sound. (CD) Play <&>CD track in (E) way on channel2
; <&> : AUDIO\044
; (E) : Single playback
; Values to add in script command: $2000, 129, $2C

; Set Trigger Type - FLIPEFFECT 223
; Exporting: TRIGGER(0:0) for FLIPEFFECT(223)
; <#> : Show Statistics Screen
; <&> : 
; (E) : 
; Values to add in script command: $2000, 223, $0

; Set Trigger Type - FLIPEFFECT 82
; Exporting: TRIGGER(2:0) for FLIPEFFECT(82)
; <#> : Delay. Load <&>level in (E)seconds
; <&> : 2 
; (E) : Forever (use other action/effect to disable it)
; Values to add in script command: $2000, 82, $2

So that's what happens when TriggerGroup#1 has just been activated:

1. The background audio stops.

2. Then that 'typical audio' of audio folder starts. - In this example that audio file has ID 044. (But it could be anything else.)

3. Then the game freezes and the Statistics Screen pops up.

4. Hit ESC. - This is the way to close Statistics Screen and make the TriggerGroup go on.
So, if you hit ESC, then the Statistics closes and the game starts to load the level that has the given [Level] block in Script. In this example that block is the second one. (But it could be anything else.)

Notes:

1. Now we use 'single playback' for the typical audio, as in TR2. But you can use loop mode, as in TR3, if you want. (Still on channel2.)

2. Nothing will be activated if the game awaits the player to hit ESC.
That's why you can't place the audio triggers between the triggers of the Statistics and the level-jump.

3. When the player hits ESC then the game will be active for a short moment, after the frozen state expired, before the loading starts.
In some situations it could be a bit ugly. - Just think about it with this example:
Lara has just jumped back, to grab a ledge and hang down. Under her, there's the square with the TriggerGroup#1-activator trigger. So the game freezes, the Statistics Screen pops up. The player releases CTRL and hit ESC. The game will be active for a moment so the player will see Lara falling down before the level-jump, because CTRL isn't just pushed down.

4. It's not a problem if you use that setup at the 'temporary end' of a level - i.e. if you can get back to that level later.
Moreover, actually, you can use the setup anywhere in the level (reform that to work properly under the new circumstances: for example, you don't need level-jumping trigger in this case, of course), 'just to show the Statistics for the player, before (s)he continues playing'.

5. Don't forget to clear up and adjust the usual specialties of the level-jump:
Is ResetHUB needed?
Is Lara just riding a motorbike when jumping to the other level?
Etc.

New type statistics

You don't need to use the Statistics Screen everyway. I.e. you can use your own statistics, if you want.
Now I show in a simple example how to make your own statistics:

a, You will use a background picture where the statistics entries will be written on. - Save the picture in Pix subfolder of Level Editor main folder, with name 'Image', an ID and BMP extension, for example, as Image11.bmp.
I mean, in fact, this is only a frame, because the picture will cover the texts, so the picture is transparent at the points where it is overlapped with the texts. - This time the frame is very simple:

The magenta part should be transparent in the game.

You need an Image command in Script for that bmp:

Image= 1, 11, IF_TRANSPARENCE+IF_QUIT_ESCAPE, IGNORE, IGNORE, 250, 250, 500, 500

The Image#1 command says it will use Image11.bmp, placed it in '250, 250, 500, 500' position. IF_TRANSPARENCE means the magenta part will be transparent.
The presence of the picture will freeze the game (if it doesn't, then the Image#1 command had an IF_POP_IMAGE constant).
IF_QUIT_ESCAPE means you will delete the picture off the screen by hitting ESC.

The picture will be drawn on the screen by this trigger:

; Set Trigger Type - FLIPEFFECT 217
; Exporting: TRIGGER(1:0) for FLIPEFFECT(217)
; <#> : Images. Show image with data in <&>Image script command for (E)Seconds
; <&> : Image= 1
; (E) : Forever (use other action/effect to disable it)
; Values to add in script command: $2000, 217, $1

----------------------------------

b, The Statistics will show now these things:

- title ('Statistics') with the name of the level
- how many big medipacks Lara's used in the level so far
- how many enemies Lara's shot in the level so far

This trigger will print the title and the level name on the screen:

; Set Trigger Type - FLIPEFFECT 203
; Exporting: TRIGGER(256:0) for FLIPEFFECT(203)
; <#> : Text. Print formatted text <&>ExtaNg string with (E)formatting data
; <&> : 0: Statistics\nCOASTAL RUINS
; (E) : Parameters=PARAM_PRINT_TEXT, 1
; Values to add in script command: $2000, 203, $100

As you see, the title and the level name come from ExtraNG #0 entry now. ('\n' means: 'I hit ENTER'.)
This is the PARAM_PRINT_TEXT that the trigger uses:

Parameters= PARAM_PRINT_TEXT, 1, CL_RED, IGNORE, IGNORE, IGNORE, 300, 300

So the text will be red, and in '300, 300' position.

----------------------------------

The medipack amount will be calculated with this GlobalTrigger now:

GlobalTrigger= 1, IGNORE, GT_USED_BIG_MEDIPACK, IGNORE, IGNORE, 4, IGNORE

So, if Lara uses a big medipack then TriggerGroup#4 will be activated:

TriggerGroup= 4, $2000, 231, $148

; Set Trigger Type - FLIPEFFECT 231
; Exporting: TRIGGER(328:0) for FLIPEFFECT(231)
; <#> : Variables. Numeric. Add to <&>Variable the (E)value
; <&> : Local Byte Delta1
; (E) : Value 1
; Values to add in script command: $2000, 231, $148

It means 1 will be added to Local Byte Delta1 variable every time when Lara uses a big medipack.

This trigger will print the entry of the big medipack on the screen:

; Set Trigger Type - FLIPEFFECT 203
; Exporting: TRIGGER(513:0) for FLIPEFFECT(203)
; <#> : Text. Print formatted text <&>ExtaNg string with (E)formatting data
; <&> : 1: Big Medipack Used #0048
; (E) : Parameters=PARAM_PRINT_TEXT, 2
; Values to add in script command: $2000, 203, $201

As you see, the 'Big Medipack Used' text and its actual value (#0048 means the value of Local Byte Delta1) - i.e. how many big medipacks Lara's used so far in the level - come from ExtraNG #1 entry now.
This is the PARAM_PRINT_TEXT that the trigger uses:

Parameters= PARAM_PRINT_TEXT, 2, CL_GOLD, IGNORE, IGNORE, IGNORE, 300, 400

So the text will be colored gold, and in '300, 400' position.

----------------------------------

The killed (shot) enemies will be calculated this way now:

First of all, place this trigger on a square where LARA object is placed:

; Set Trigger Type - FLIPEFFECT 244
; Exporting: TRIGGER(20560:0) for FLIPEFFECT(244)
; <#> : Variables. Memory. Copy to <&>Numeric Variable the (E)Savegame Memory value
; <&> : Local Short Alfa1
; (E) : Statistics. Killed Enemies (Short)
; Values to add in script command: $2000, 244, $5050

So, if Lara starts this level then the data about how many enemies she's killed so far will goes to Local Short Alfa1 variable. (Be careful: this trigger is not allowed to be activated again.)

When the level ends, this trigger records the data about how many enemies Lara's killed so far - and puts the value into Current Value variable:

; Set Trigger Type - FLIPEFFECT 244
; Exporting: TRIGGER(20735:0) for FLIPEFFECT(244)
; <#> : Variables. Memory. Copy to <&>Numeric Variable the (E)Savegame Memory value
; <&> : Current Value
; (E) : Statistics. Killed Enemies (Short)
; Values to add in script command: $2000, 244, $50FF

Then this trigger subtracts the starting value (Local Short Alfa1) from the ending value (Current Value), giving a new value to Current Value:

; Set Trigger Type - FLIPEFFECT 286
; Exporting: TRIGGER(80:0) for FLIPEFFECT(286)
; <#> : Variables. Numeric. Subtract from CurrentValue the <&>Numeric Variable
; <&> : Local Short Alfa1
; (E) : 
; Values to add in script command: $2000, 286, $50

(So, for example, if the starting value is 18 and the ending value is 27, then 27-18=9 will be the new value of the Current Value that means Lara's shot 9 enemies in the level.)

This trigger will print the entry of the killed enemies on the screen:

; Set Trigger Type - FLIPEFFECT 203
; Exporting: TRIGGER(770:0) for FLIPEFFECT(203)
; <#> : Text. Print formatted text <&>ExtaNg string with (E)formatting data
; <&> : 2: Enemy Killed #0800
; (E) : Parameters=PARAM_PRINT_TEXT, 3
; Values to add in script command: $2000, 203, $302

As you see, the 'Enemy Killed' text and its actual value (#0800 means the value of Current Value) come from ExtraNG #2 entry now.
This is the PARAM_PRINT_TEXT that the trigger uses:

Parameters= PARAM_PRINT_TEXT, 3, CL_GOLD, IGNORE, IGNORE, IGNORE, 300, 450

So the text will be colored gold, and in '300, 450' position.

(See more about these Script commands and constants in NG Center\Reference, and about the variables in Variables demo project of Paolone.)

And the solution is:

Attention!
As you see, the example above shows level data and not game data.

And now, let's see the whole script setup:

TriggerGroup= 1, $2000, 127, $1
TriggerGroup= 2, $2000, 130, $0, $2000, 129, $2C, $2000, 203, $100, $2000, 203, $201, >
$2000, 244, $50FF, $2000, 286, $50, $2000, 203, $302
TriggerGroup= 3, $2000, 217, $1, $2000, 82, $2
TriggerGroup= 4, $2000, 231, $148
Organizer= 1, FO_TICK_TIME, IGNORE, 0, 2, 1, 3
Image= 1, 11, IF_TRANSPARENCE+IF_QUIT_ESCAPE, IGNORE, IGNORE, 250, 250, 500, 500
Parameters= PARAM_PRINT_TEXT, 1, CL_RED, IGNORE, IGNORE, IGNORE, 300, 300
Parameters= PARAM_PRINT_TEXT, 2, CL_GOLD, IGNORE, IGNORE, IGNORE, 300, 400
Parameters= PARAM_PRINT_TEXT, 3, CL_GOLD, IGNORE, IGNORE, IGNORE, 300, 450
GlobalTrigger= 1, IGNORE, GT_USED_BIG_MEDIPACK, IGNORE, IGNORE, 4, IGNORE

And that's what will happen in the game:
First of all, Lara activates TRIGGER(20560:0) for FLIPEFFECT(244) trigger at the start of the level to define the killed enemies actual value.
Later, when Lara activates TriggerGroup#1 at the end of the level by TRIGGER(257:0) for FLIPEFFECT(118) trigger, then it means Organizer#1 will be activated ($2000, 127, $1). - We need an Organizer to split the operation into two parts with some time slip now, or else it won't work properly. (As you see - by FO_TICK_TIME constant and the second 1 in the Organizer= 1 command - the time slip is very small, only 1 frame.)
The first part of the operation is defined in TriggerGroup#2 that is activated at once (see '0, 2' in Organizer= 1) when Lara reaches the end of the level.
TriggerGroup#2 stops the background audio ($2000, 130, $0), starts 044.wav ($2000, 129, $2C), prints the title+level name ($2000, 203, $100), and prints the bigmedi data ($2000, 203, $201). Then calculates the 'killed enemies' data ($2000, 244, $50FF, $2000, 286, $50) and prints that ($2000, 203, $302).
The second part of the operation is defined in TriggerGroup#3 that is activated (see '1, 3' in Organizer= 1) 1 frame after TriggerGroup#2 has been activated.
TriggerGroup#3 puts Image11.bmp on the screen ($2000, 217, $1), and - if the player hits ESC so that TriggerGroup#3 can continue - loads Level#2 ($2000, 82, $2).

Note:

Be cautious with the setup if it contains level data and not game data.
For example, do you want that value of the big medipack (Local Byte Delta1) to be zero when Lara comes back to the level? Because, if you make that zero (somehow), then the Statistics will show you partial data when Lara leaves the level (finally, this time). But if you don't make that zero then the partial data of the first level part and the partial data of the second level part will be added in the Statistics at the final end of the level and shows total big medipack data on the level.

Forming the texts

As you see above, you can do text-forming customization on your Statistics (color, position etc.) - anyway, whether you use the original Statistics or some new one.
Caring about forming texts is a bit complicated in NGLE so I decided I'd show you all the forming rules, and not only the ones about the Statistics.

First of all, I'll define the text types I'll use in the description below:

- OLD TEXT: texts you can also find in TRLR or TRLE: texts in the inventory, in the Statistics Screen etc.

- NGLE TEXT: texts (except Ammo and Feature Text) that the new NGLE features use: the amount of done rotations (parallel bars), the text for the standby effect, the timer of 'Enemy. Timer. Show the trigger count-down for <#>enemy using (E)format' ACTION trigger etc.

- AMMO TEXT: text that is used for showing ammo amount on the screen (this time it is not the ammo amount in the inventory)

- FEATURE TEXT: texts of text/picture NGLE features (diary, new savegame panel)

- INDEPENDENT TEXT: texts that you write mostly into ExtraNG section of Strings to print them by Text FLIPEFFECTs

To form the texts, these are the tools:

- Defining basic fonts (except Feature Text) for all the levels of the game: see font.pc in graphics\wads folder.

- Defining fonts (except Feature Text) for a given level: put a FONT_GRAPHICS object into the WAD of the level (see: new font demo project of Paolone), then use NG Font Editor in NG Center\Tools.

- Defining fonts for Feature Text: see WindowsFont Script command.

- Customizing Old Text colors: see CUST_SET_TEXT_COLOR Script constant. (It works only on the kind of Old Text you give in the command.)
Customizing of the level texts, the customization command must be placed in a [Level] block usually.
Customizing of the level texts - if you want the validity of the customization for the whole game - or the title texts, the customization command must be placed in [Title] block.

- Customizing (in Script) Old Text sizes: see TextFormat Script command (only SizeCharacterMenu field is valid now), placing that in [Title] block. Valid for all the Old Texts of the game (except: the text of Legend Script command).

- Customizing (in Script) NGLE Texts: see TextFormat command (every field is valid now, except SizeCharacterMenu), placing that in a [Level] block. - Using an NGLE feature like that in the title you can use TextFormat in [Title] block to form the text of the feature.
(I think this operation doesn't work on all the NGLE texts. It works, for example, for the standby effect text or the enemy timer etc.)

- Customizing (in the game) NGLE Texts, if they can be affected by TextFormat command: use 'Text. Set' FLIPEFFECTs. It overwrites (on that level) TextFormat command values or the latest similar 'Text. Set' trigger of that level. It works for the texts that are just written or the texts that will be written. It is valid only on that level, until the next FLIPEFFECT like that.

- Annulling (on the actual level) every effect of TextFormat command or all 'Text. Set' FLIPEFFECTs for the NGLE Texts that are just written or the NGLE Texts that will be written: see 'Text. Reset all text formatting settings with default values' FLIPEFFECT.

- Customizing Ammo Text: see CUST_SHOW_AMMO_COUNTER Script constant. Place it in a [Level] block usually. Place it in [Title] block, if you want it to be valid for the whole game.

- Customizing (in Script) 'A' type Independent Texts: see TextFormat command (every field is valid now, except SizeCharacterMenu), placing that in a [Level] block. - Using a text like that in the title level you can use TextFormat in [Title] block to form the text.
'A' type Independent Texts are the texts that will be written on the screen by a FLIPEFFECT that doesn't use a PARAM_PRINT_TEXT Script constant.

- Customizing (in the game) 'A' type Independent Texts: use 'Text. Set' FLIPEFFECTs. It overwrites (on that level) TextFormat command values or the latest similar 'Text. Set' trigger of that level. It works for the texts that will be written. It is valid only on that level, until the next FLIPEFFECT like that. - Only the proper FLIPEFFECT can be used.

- Annulling (on the actual level) every effect of TextFormat command or all 'Text. Set' FLIPEFFECTs for the 'A' type Independent Texts that will be written: see 'Text. Reset all text formatting settings with default values' FLIPEFFECT.

- Customizing 'B' type Independent Texts: see PARAM_PRINT_TEXT Script constant.
'B' type Independent Texts are the texts that will be written on the screen by a FLIPEFFECT that uses a PARAM_PRINT_TEXT Script constant.

Made using TRNG 1.2.2.6