Checkpoints
by AkyV
Download a project file illustrating this tutorial

You'll be able to use checkpoint type game savings if you follow the description in this tutorial. 'Checkpoint type' means all of this now:

- 'Saving crystal' objects mark the places of the checkpoints.
- An automatic game saving will happen (in an unseen way) when Lara reaches a checkpoint.
- You have a manual method and (if Lara dies) an automatic method to load Lara (loading an automatic game saving) to the latest checkpoint she reached so far.
- You can use the usual (F5/F6) type game saving-loading system, but not in the usual way:

a, The first method I'll show works this way: you can save only at checkpoints. So, if you load a saved position then you'll always get back to a checkpoint.
b, The second method I'll show works this way: you can save at any point of the level. But if you load then the game won't load Lara to the exact point where this saving happened but load her to the checkpoint she reached last before this saving.

Notes:

1. Loading to a checkpoint by F6 makes that checkpoint become the latest checkpoint.

2. The second F5/F6 method is obviously cooler than the first one. But the second method has some unpreventable bugs. (Not game-killing bugs but 'only' annoying bugs - that's why I decided I'd show you the second method.) - Sorry, I tried many things, but I couldn't prevent those bugs.

3. This tutorial is made by TRNG 1.2.2.6

PART 1
THE BASICS

If you don't understand anything then see Part 2: Advanced Settings.
1.1. MAKING A CHECKPOINT
You'll use a life energy crystal of TR3 as a saving crystal:

1. Take a life energy crystal from any TR3 WAD and copy it into the WAD of your level, into any ANIMATING slot. (For example, ANIMATING1.)
2. Try this in the game: if the bounding box of this object is enabled (i.e if Lara can't get through the object) then disable it, clicking on Disable Collesion button of WADMerger (when this object is marked in the left window).
3. Place one ANIMATING1 on each square of your plan where you want to see a checkpoint.
4. Trigger the crystals to animate them.
5. Place one FLIPEFFECT trigger on each of those squares: 'Backup. Save in silent way the current game in <&> backup file'. &= Game Backup 1. - When Lara activates this trigger then a game saving will happen: not in one of the usual savegame slots but in this 'Game Backup 1' slot.

Notes:

1. Maybe the crystal is too close to the floor so you'd better raise it up a bit.

2. Stop the animation of the crystals that are just out of the player's POV to save some memory.
Then start their animations again if the player can see them again.

3. Maybe it looks nice if there's some light effect of every crystal. You can adjust that if you use an AddEffect script command.

4. So, as you can see, you always have to use this Game Backup 1 slot at every checkpoint in the game.

5. When the trigger saves the position at the checkpoint then Lara can be anywhere on that square from the floor to the ceiling. If you don't want that then you can use some condition on that square:
For example, you don't want the checkpoint to save the position when Lara is moving over the square with using monkey bars. In this case you can use for example Vertical trigger CONDITION or 'Lara. (Status) Lara is performing <#>action is (E)' CONDITION trigger to prevent game saving.

6. You can place some additional triggers on the square of the checkpoint:

a, Use a Text FLIPEFFECT to write text 'CHECKPOINT' on the screen for some seconds when Lara's on the square of a checkpoint.
b, Use a Sound FLIPEFFECT to play a (very short) audio or sound file, marking this is a checkpoint. (Just as a typical audio file marks the secrets.) - You'd better use a sound. Because when you use an audio, it will interrupt another audio that is just playing, when Lara reaches a checkpoint.
(When you choose that sound then be careful with the details: for example, the chance of the playing - i.e. the value of CH - should be 0, i.e. 100 %. If it's not 0 then this sound won't always play when Lara reaches a checkpoint.
By the way I chose EXPLOSION1 - ID: 105 - sound for this tutorial. But it's only an example, of course.)

If you use (one of) these triggers at a checkpoint, then you need use that/those trigger(s) at all of the checkpoints, definitely.

7. If you use this crystal in your WAD as a life energy crystal then you won't have problems if you use that crystal in another ANIMATING slot, too, as a saving crystal. (The different look is recommended, though. - I.e. you need to retexture the life energy crystals or the saving ones. - But only because of logical reasons: the player won't confuse the two crystal types with each other this way. And this is also more aesthetical, of course.)

1.2. LOADING TO THE LATEST CHECKPOINT

1.2.1. In a manual way


Use these rows in Script - not only in all [Level] blocks but also in [Title] block:

GlobalTrigger= 1, IGNORE, GT_KEYBOARD_CODE, 46, IGNORE, 1, IGNORE
TriggerGroup= 1, $2000, 98, $1

GT_KEYBOARD_CODE constant says the condition is true when the player hits a key on the keyboard.
In NG Center\Reference\Keyboard Scancodes list you'll find a list about the codes of the keys that you can use in script commands. As you see, the code 46 is the key C.

So this GlobalTrigger says: 'if the player hits C (C as Checkpoint) in any level or title then the TriggerGroup#1 will happen, i.e. the game will load Lara to the latest checkpoint she reached so far'.

; Exporting: TRIGGER(1:0) for FLIPEFFECT(98)
; <#> : Backup. Restore (load) the <&>backup file in (E)way
; <&> : Game Backup 1
; (E) : Standard way (progress bar + load camera screen)
; Values to add in script command: $2000, 98, $1


Notes:

1. Until the very first saving at the very first checkpoint this Game Backup type loading will load to title. (Place a checkpoint on the square where LARA object is placed in the very first level of the game to prevent this bug.)

2. You have to be careful with placing checkpoints. - Two examples will explain it why:

- Lara's just burning when her position will be saved at a checkpoint. She dies (there's no water anywhere to extinguish her) and the game loads her automatically to title. If the player loads this checkpoint (the latest checkpoint) with hitting C then finds Lara here burning. So Lara dies here again and the games loads again automatically.

- Lara's jumping over a checkpoint and arrives into a pit. The pit is too deep and there's no ladder or other tool that can help her to get out of the pit. The player thinks: 'that doesn't matter. I'll load that checkpoint, and the game will put Lara on that point'. But, if the player loads the game, Lara shows up jumping, and then falls into the pit again.

3. You should inform the player about the new features just like this key C. You have these possibilities for that:

- You'll make a Readme file for your game.
- You'll use a FLIPEFFECT to place a text (or an image with a text on it - see Image script command) on the screen. In the present case something like this: 'Hit key C to load to the latest checkpoint'.
- You'll use a Diary script command to edit an in-game manual for your game.


1.2.2. In an automatic way

Use this row in Script - only in all [Level] blocks:

GlobalTrigger= 2, IGNORE, GT_LARA_HP_LESS_THAN, 1, IGNORE, 1, IGNORE

This GlobalTrigger says: 'if Lara has (almost) died then the TriggerGroup#1 (see above) will happen, i.e. the game will load Lara to the latest checkpoint she reached so far'.

'Almost' means this method is wrong a bit, because the load starts when Lara starts dying. So you won't see the whole dying animation, it will be interrupted and the load will be started.

If this interruption bothers you then you must avoid the automatic loading: don't use that row in Script.
In this case you will get back to title if Lara dies (as usually in NGLE).


1.3. USING FIRST USUAL SAVING-LOADING METHOD

1.3.1. Using Save Game function


You have to place this trigger in every level on the square where LARA object is placed:

; Exporting: TRIGGER(18:0) for FLIPEFFECT(51)
; <#> : Keyboard. Disable <&>keyboard command for (E) time
; <&> : Save the game (special)
; (E) : Forever (use other action/effect to disable it)
; Values to add in script command: $2000, 51, $12


So you're not allowed to save by the usual system in the whole level. Except at the checkpoints.
An exception like that is marked with one trigger that is placed on the square of each checkpoint:

; Exporting: TRIGGER(18:0) for FLIPEFFECT(53)
; <#> : Keyboard. Simulate receivement of <&>keyboard comand in (E) way
; <&> : Save the game (special)
; (E) : Single sending
; Values to add in script command: $2000, 53, $12


So when Lara reaches that checkpoint then the savegame panel pops up (by this trigger) and you can save if you want. (The disabled state of saving by the usual system won't prevent the savegame panel to pop up or you to save on this panel now.) Or you can hit ESC to close the panel without saving if you don't want.

Note:

Using this saving-loading method it doesn't matter whether if you use the 'classic' savegame panel or (see SavegamePanel script command) the new one.


1.3.2. Using Load Game function

The loading by F6 will work properly if you use these rows in Script, only in all [Level] blocks:

GlobalTrigger= 5, IGNORE, GT_LOADED_SAVEGAME, IGNORE, IGNORE, 10, IGNORE
TriggerGroup= 10, $2000, 97, $1, $2000, 70, $1F69

$2000, 70, $1F69 is the trigger (see above) to play sound 105. If you use another trigger to play another sound/audio file at your checkpoints then use that trigger here and not $2000, 70, $1F69.
If you don't use any sound/audio at your checkpoints then just clear $2000, 70, $1F69 here.

If you want to know more about this then see advanced settings.


1.4. USING SECOND USUAL SAVING-LOADING METHOD

This method is an advanced setting.

PART 2
ADVANCED SETTINGS

2.1. LOADING TO THE LATEST CHECKPOINT

2.1.1. In a manual way


Some advanced notes to the theme:

1. If you don't understand why we call Game Backup 1 position 'the latest checkpoint she reached so far' then have a look at the picture of this example:

arrow: Lara's route
C8: this is the 8th checkpoint of the level: checkpoint#8 (saves position in Game Backup 1 slot any time when Lara reaches this point)
C9: this is the 9th checkpoint of the level: checkpoint#9 (saves position in Game Backup 1 slot any time when Lara reaches this point)
P8: this is a point anywhere in the level. If the player hits C here then the game will load Lara to the position of Game Backup 1 that is saved at the latest checkpoint she reached so far: checkpoint#8. If the player doesn't then Lara will go on to C9.
P9: this is a point anywhere in the level. If the player hits C here then the game will load Lara to the position of Game Backup 1 that is saved at the latest checkpoint she reached so far: checkpoint#9. If the player doesn't then Lara will go on.

2. I mentioned this problem above: the game loads the checkpoint, but Lara shows up jumping, and then falls into the pit again.
Well, I tried to prevent these kinds of problems, forcing Lara to have a fixed and standard - for example, standing - animation always after the game has loaded her to a checkpoint, but it doesn't work properly, because the game won't abort an animation - for example, that jumping - right after loading. - By the way, knowing that problem, I think it's not even worth caring about other fixed settings - position on the square, facing - that Lara could have when the game has loaded her to a checkpoint.

3. I mentioned above how useful the Readme file, the text on screen or a diary to inform the player - also about any preventable bugs (if you have some, not only in this theme), telling the player what to do to prevent.


2.1.2. In an automatic way

If the interruption mentioned above (see Chapter 1.2.2.) bothers you but you don't want to avoid the automatic loading then you have to know that you can't use Lara's exactly dead status as a condition (see: $8000, 0, $1D) for that GlobalTrigger because every trigger will be ignored after Lara's death. Including the loading trigger, of course.

; Exporting: CONDITION(29:62) for PARAMETER(0)
; <#> : Vitality= 0
; <&> : Lara. (Health) Lara vitality is (E)Condition than <#>vitality
; (E) : Equal than ...
; Values to add in script command: $8000, 0, $1D


So you have only this choice to use the automatic loading without that interruption:

You don't use that GlobalTrigger in Script, but you export that $2000, 98, $1 loading trigger as an AnimCommand. Then add that AnimCommand to all (!) the dying animations of Lara in WADMerger. (Actually it means not one but more AnimCommands if you add this trigger to frames that have diverse numbers. I.e., for example, if you add the trigger to frame60 at one animation and frame70 at another animation.)
Always add this AnimCommand to the latest frame just before the loop end of the animation:
For example, see Animation149: 'impaling by teeth spikes' dying animation that has 86 frames. It has these values: Next Animation= 149 and Next Frame= 86. It means the animation will be looped at the end (just as every dying animation), i.e. the animation will 'freeze' at the end. - So you have to place the AnimCommand just before this 'looping frame', at frame85.

Anyway, this is still not a perfect method. Because the loading will start just when the dying animation is completed. So in this case the game will skip the short pause between the end of the dying animation and the start of the loading that we got used to.
If you miss this pause then you'll export $2000, 148, $64 trigger, as another AnimCommand:

; Exporting: TRIGGER(100:0) for FLIPEFFECT(148)
; <#> : Text. Print ExtraNG <&>string on screen, freeze game and wait Escape
; <&> : 100: 
; (E) : 
; Values to add in script command: $2000, 148, $64


Place this trigger to the frame just before the frame of the loading: this frame is frame84 now.
This trigger will print a text on the screen, and freeze the game until the player hit key ESC. This time you use ExtraNG string#100 to print, but you can see: this string is empty now. So, the game will freeze (without any text printing on the screen), and when the player hits ESC then the game will step to the next frame of the animation, i.e. start loading.

If one of these two AnimCommands doesn't work (properly) at an animation then use bigger space between them at that animation. (For example, two frames space instead of one frame.)
And/or use these AnimCommands only at the frames that you can see on the frame bar under the Animation Editor window. (For example, if FrameRate=2 and you see frames 68, 70, 72 etc. there then don't use frames 69, 71 etc.)

Use this AnimCommand-type method only if those AnimCommands work properly at all the dying animations!

Notes:

1. As some further correction, you can think something like this:
'This animation ends at frame86, but the freezing breaks it at frame84. I'll add more frames to the animation: frame87 with that freezing trigger, frame88 with that loading trigger and frame89 as the new looping frame. So I'll see the whole original animation until frame86 without any bothering AnimCommands, because I'll delete them from frame84 and 85. And I won't miss anything with breaking at those new frames because Lara is still - i.e. dead - at frame86 and the new frames are the copies of frame86 so Lara will be still from frame86 to frame89'.

2. Be careful: if you have the same animations in two or more WADs then these animations don't always have the same FrameRate value.


2.2. USING FIRST USUAL SAVING-LOADING METHOD

Maybe you think you'll reform this method (see Chapter 1.3.): you won't use that popping-up trigger but you will form a 'frame' around the checkpoint: this 'frame' is the eight squares just around the square of the checkpoint. The 'frame' is composed of those disabling triggers. And this time you also have an enabling FLIPEFFECT ('Keyboard. Enable newly <&>keyboard command', &=Save the game (special)) on the square of the checkpoint.
This way this is what will happen: when Lara reaches the checkpoint then you can save there if you want, hitting F5. But if Lara leaves the checkpoint then you can't save again.

But this would be a problematic reform, because of these two reasons:

- When opposed (on/off) triggers respecting the same thing (i.e. saving by F5 this time) are really close to each other (i.e. in adjacent squares) then this setup doesn't always work properly if Lara moves straight from one trigger to the other trigger. (I think it's - mostly? - a FLIPEFFECT malfunction.)

- Just think about an event that is about a fast riding with a motorbike (because you have to do something during some short time). You don't have any time to stop at the checkpoint you're just driving through and save there. But if you're just driving through there fast, hitting F5, then maybe you hit F5 too soon or too late, but not exactly at that checkpoint, so you'll miss the opportunity to save with F5 at that checkpoint.


2.2.1. Switching on and off the savings

Be careful with placing checkpoints. See this example: Lara's chasing an enemy but a checkpoint gets in her way and the savegame panel pops up, distracting the player.
If you don't want the popping-up to bother you in some part of the game then you can switch off the popping-up temporarily if you hit F2 (losing the chance to save at the checkpoints by the usual system) and switch that on again if you hit F1. - But only after you did these three things:

1. Place only (!) these triggers on the square of each checkpoint (so clear every other trigger there):

- Condition trigger(s) that is/are customized for that checkpoint. (See, for example, that Vertical trigger CONDITION above.)
- A trigger that activates TriggerGroup#9:

; Exporting: TRIGGER(265:0) for FLIPEFFECT(118)
; <#> : TriggerGroup. Perform <&>TriggerGroup from script.dat in (E)way
; <&> : TriggerGroup= 9
; (E) : Single performing (to use when in TriggerGroup there are only commands)
; Values to add in script command: $2000, 118, $109


Note:

Because of some strange reason, we have to use �single performing' value in E window of the trigger, in spite of that, as you'll see below, there's a condition in the trigger, so - according to the general rules - we should use �multiple performing' value there. (But the trigger wouldn't work properly with �multiple' value now.)

2. Use these entries in Script, only in all [Level] blocks:

GlobalTrigger= 2, IGNORE, GT_KEYBOARD_CODE, 59, IGNORE, 10, IGNORE

GlobalTrigger= 3, IGNORE, GT_KEYBOARD_CODE, 60, IGNORE, 3, IGNORE
GlobalTrigger= 4, FGT_SINGLE_SHOT_RESUMED, GT_CONDITION_GROUP, IGNORE, 4, 5, IGNORE
GlobalTrigger= 13, FGT_SINGLE_SHOT_RESUMED, GT_CONDITION_GROUP, IGNORE, 7, 6, IGNORE
TriggerGroup= 10, $2000, 234, $48
TriggerGroup= 3, $2000, 235, $48
TriggerGroup= 4, $8000, 72, $2C
TriggerGroup= 7,$8000, 72, $2D
TriggerGroup= 5, $2000, 204, $15, $2000, 66, $202, $2000, 64, $14
TriggerGroup= 6, $2000, 204, $14, $2000, 66, $202, $2000, 64, $15
TriggerGroup= 9, $2000, 97, $1, $2000, 66, $102, $2000, 64, $210, >
$2000, 70, $1F69, $8000, 72, $2C, $2000, 53, $12

The GlobalTrigger#2 says if the keyboard code is 59 (key F1) then TriggerGroup#2 will be activated - i.e. Local Byte Delta1 variable gets bit 0 (I mean the variable gets value 1):

; Exporting: TRIGGER(72:0) for FLIPEFFECT(234)
; <#> : Variables. Numeric. Set in <&>Variable the (E)bit
; <&> : Local Byte Delta1
; (E) : Bit 0 ($00000001 ; 1)
; Values to add in script command: $2000, 234, $48


The GlobalTrigger#3 says if the keyboard code is 60 (key F2) then TriggerGroup#3 will be activated - i.e. Local Byte Delta1 variable loses bit 0 (I mean the variable gets value 0):

; Exporting: TRIGGER(72:0) for FLIPEFFECT(235)
; <#> : Variables. Numeric. Clear in <&>Variable the (E)bit
; <&> : Local Byte Delta1
; (E) : Bit 0 ($00000001 ; 1)
; Values to add in script command: $2000, 235, $48


The GlobalTrigger#4 says if the condition is true in TriggerGroup#4 then TriggerGroup#5 will happen:

; Exporting: CONDITION(44:62) for PARAMETER(72)
; <#> : Local Byte Delta1
; <&> : Variables. The <#>Numeric Variable has the (E)Bit set
; (E) : Bit 0 ($00000001 ; 1)
; Values to add in script command: $8000, 72, $2C

This condition examines if Local Byte Delta1 has bit 0 (value 1).

The GlobalTrigger#13 says if the condition is true in TriggerGroup#7 then TriggerGroup#6 will happen:

; Set Trigger Type - CONDITION 45
; Exporting: CONDITION(45:62) for PARAMETER(72)
; <#> : Local Byte Delta1
; <&> : Variables. The <#>Numeric Variable has the (E)Bit clear
; (E) : Bit 0 ($00000001 ; 1)
; Values to add in script command: $8000, 72, $2D


This condition examines if Local Byte Delta1 has not bit 0 (value 1).

The TriggerGroup#5 and #6 are the opposite of each other:
TriggerGroup#5 clears 'savegames off' text (the content of ExtraNG string#21) off the screen and prints 'savegames on' text (the content of ExtraNG string#20) on it:


; Exporting: TRIGGER(21:0) for FLIPEFFECT(204)
; <#> : Text. Print. Remove (&)Extra NG String from screen
; <&> : 21: savegames off
; (E) :
 
; Values to add in script command: $2000, 204, $15

; Exporting: TRIGGER(20:0) for FLIPEFFECT(64)
; <#> : Text. Print ExtraNG <&>string on screen for (E) seconds
; <&> : 20: savegames on
; (E) : Forever (use other action/effect to disable it)
; Values to add in script command: $2000, 64, $14


TriggerGroup#6 clears 'savegames on' text off the screen and prints 'savegames off' text on it:

; Exporting: TRIGGER(20:0) for FLIPEFFECT(204)
; <#> : Text. Print. Remove (&)Extra NG String from screen
; <&> : 20: savegames on
; (E) :
 
; Values to add in script command: $2000, 204, $14

; Exporting: TRIGGER(21:0) for FLIPEFFECT(64)
; <#> : Text. Print ExtraNG <&>string on screen for (E) seconds
; <&> : 21: savegames off
; (E) : Forever (use other action/effect to disable it)
; Values to add in script command: $2000, 64, $15


So the GlobalTrigger#4 says if Local Byte Delta1 is 1 then the game will clear 'savegames off' text and prints 'savegames on' text.
And the GlobalTrigger#13 says if Local Byte Delta1 is not 1 then the game will clear 'savegames on' text and prints 'savegames off' text.

So if you hit F1 then you can see 'savegames on' or when you hit F2 then you can see 'savegames off'.

; Exporting: TRIGGER(514:0) for FLIPEFFECT(66)
; <#> : Text. Set <&>color and (E)position for next Print String flipeffect
; <&> : White
; (E) : Top line, central alignment
; Values to add in script command: $2000, 66, $202


This trigger - using the default white color - puts 'savegames on' and 'savegames off' texts on the top of the screen. (If we left them on the default position then these texts would be overlapped with 'CHECKPOINT' text.)
(Maybe you think you don't need FGT_SINGLE_SHOT_RESUMED flag and you can merge GlobalTrigger#4 and #13 in this:

GlobalTrigger= 4, IGNORE, GT_CONDITION_GROUP, IGNORE, 4, 5, 6

No, believe me, it won't work. Because of technical reasons. I'd rather skip the explanation now.)

The 'savegames on' means you can save with usual saving system at the checkpoints because the savegame panel will pop up there.
The 'savegames off' means you can't save with usual saving system at the checkpoints because the savegame panel won't pop up there.

But why do these texts mean that?

Well, now the TriggerGroup#9 is what contains that trigger for popping-up (see $2000, 53, $12 above) and also contains a condition trigger ($8000, 72, $2C) for the popping-up trigger. If the condition in $8000, 72, $2C trigger is true (i.e. being after hitting F1, when Local Byte Delta1 is 1) then the savegame panel will pop up if Lara reaches that checkpoint, or, if the condition's not true (i.e. being after hitting F2, when Local Byte Delta is not 1), it won't pop up.

So these things happen (in this order) when Lara activates TriggerGroup#9, reaching a checkpoint:

1. This trigger saves the game in Game Backup 1 slot:

; Exporting: TRIGGER(1:0) for FLIPEFFECT(97)
; <#> : Backup. Save in silent way the current game in <&> backup file
; <&> : Game Backup 1
; (E) :
 
; Values to add in script command: $2000, 97, $1

2. This trigger confirms that the position and color of text 'CHECKPOINT' have the default values of the game.

; Exporting: TRIGGER(258:0) for FLIPEFFECT(66)
; <#> : Text. Set <&>color and (E)position for next Print String flipeffect
; <&> : White
; (E) : Bottom line, central alignment
; Values to add in script command: $2000, 66, $102


You need the trigger, because this text has not the default position value: a $2000, 66, $202 trigger - see above - has been activated before this $2000, 66, $102 trigger, and a text-forming trigger - like each of these two triggers - is always valid until a newer text-forming command.

3. This (optional) trigger prints the content of ExtraNG string#16 (i.e. text 'CHECKPOINT') on the screen for 2 seconds:

; Exporting: TRIGGER(528:0) for FLIPEFFECT(64)
; <#> : Text. Print ExtraNG <&>string on screen for (E) seconds
; <&> : 16: CHECKPOINT
; (E) : 2 seconds
; Values to add in script command: $2000, 64, $210


4. This (optional) trigger plays sound file 105, marking: this is a checkpoint:

; Exporting: TRIGGER(8041:0) for FLIPEFFECT(70)
; <#> : Sound. Play <&>Sound sample of first group (0-255) for (E) time
; <&> : EXPLOSION1 105 Ok explos1
; (E) : Perform one single time
; Values to add in script command: $2000, 70, $1F69


5. If $8000, 72, $2C condition is true, too, then the $2000, 53, $12 trigger will happen, i.e. the savegame panel will pop up - or, if it's not then it won't.

Technical notes:

- The 'savegames on' and 'savegames off' states and texts will always be saved when a game saving happens and will always be restored by loading these savings.
Let's see an example: the value is 'savegames on', and you saved on the savegame panel at a checkpoint. Then you switch this feature off. After that if you load in the usual system or hitting C to this checkpoint then the game will also load the saved value 'savegames on'.

- It's always a good idea to decrease the number of the placed and overlapped triggers with placing them in a triggergroup. (That's what we did now: as you see, this time we placed the backup-saving, CHECKPOINT-printing and sound-playing triggers in a triggergroup.)
And this way you may also prevent some malfunction that exists when you use overlapped triggers instead of having them in a triggergroup. (For example, because, you can adjust the order of activation of the triggers in a triggergroup, putting the triggers here in the proper order. - You can't - always? - do that at overlapped situations.)

- When you choose a variable for any purpose of the game (because you don't have to use exactly the same variables everyway I present in this tutorial) then don't forget these things:

a, After all, you can choose any variable if that variable is good for your purpose. (For example, variables named Alfa, Beta, Delta are good for general numeric tasks.)

b, You can use variables named Local only in the actual level. But the values of variables named Global and any other variables can be valid globally, i.e. for the whole game.
Be very careful, if you use ResetHUB, because it (always?) resets the value of the global type variables when leaving the level.

c, If you don't know what kind of byte, short or long variable to choose then see the tutorial of Variables demo project: Chapter 'The size of the Variables'.

d, Be careful: you can use the same variable for more purposes in the level, but you can use this variable only for one purpose at the same time.

e, Current Value is a global type variable that you should use only for special tasks. (For example, this is the variable that you can take a value from variable A to variable B with.) So if it's possible, don't engage Current Value for small tasks.

f, Be very careful with variables: for example, if you use task A (that has variable V1) in the level X then it works perfectly, and if you use task B (that has variable V2) in the level Y then it also works perfectly. But if you use task A and B in the level Z then the task A and/or B will work with problems maybe, because of variable V1 and V2 may disturb the working of each other in that situation.

See more about variables in their above-mentioned tutorial.

3. Place this trigger on the square where LARA object is placed:

; Exporting: TRIGGER(72:1) for FLIPEFFECT(234)
; <#> : Variables. Numeric. Set in <&>Variable the (E)bit
; <&> : Local Byte Delta1
; (E) : Bit 0 ($00000001 ; 1)
; Values to add in script command: $2000, 234, $48

This trigger adjusts 'savegames on' state as the default state of the level.
The 1 in (72:1) means 'one shot': it's important or else you'll switch this feature on unintentionally if you take Lara on this square later in the level.

Note:

Be careful: if you have a checkpoint where there's a LARA object then you can't have this trigger placed there to prevent some malfunction.


2.2.2. Using Load Game function

When the player loads a savegame slot then the game will load Lara in a position of a checkpoint - not the latest checkpoint under any circumstances but the one where the saving in this savegame slot happened:

L1: the player saved Lara's position at this checkpoint in savegame1 slot in the savegame panel. (Or any other savegame slot. It doesn't matter ever, actually.)
P: this is a point anywhere in the level. If the player hits C here then the game will load Lara to the position of Game Backup 1 that is saved at the latest checkpoint: checkpoint#5. If the player loads savegame1 slot here from the savegame panel then the game will load Lara to the checkpoint that is saved in savegame1 slot: checkpoint#4.
(P can also mean title when you got back there after Lara left C5. Or another level that Lara reached after she left C5.)

It's logical the checkpoint#4 will become the latest checkpoint after loading to that point with the usual system.
But it's problematic a bit: when Lara reaches checkpoint#4 then she will activate the 'Backup. Save in silent way the current game in <&> backup file' trigger and just after that the player will save on the savegame panel popped up. So when the player loads Lara to this on-panel saving point with Load Game function then the game will find that Backup trigger there in an activated - i.e. a non-activable - state. That's why after this loading, this checkpoint won't be the latest checkpoint instead of checkpoint#5 automatically: because that Backup-saving trigger won't be activated again.
Of course, if Lara steps off the square of that checkpoint and steps on that again at once then she will activate that trigger again, correcting the problem. But it's lame. It means we have to find a nice method to define that checkpoint as the latest checkpoint.

That's why I told you (see Chapter 1.3.2.) to use these rows in Script, only in all [Level] blocks:

GlobalTrigger= 5, IGNORE, GT_LOADED_SAVEGAME, IGNORE, IGNORE, 10, IGNORE
TriggerGroup= 10, $2000, 97, $1, $2000, 70, $1F69

So this GlobalTrigger says: 'if the loading of the game has just happened then the TriggerGroup#10 will happen, i.e. the game will save the game in Game Backup 1 slot'. That's why the checkpoint - whereto the loading has just happened - has just become the latest checkpoint.

; Exporting: TRIGGER(1:0) for FLIPEFFECT(97)
; <#> : Backup. Save in silent way the current game in <&> backup file
; <&> : Game Backup 1
; (E) : 
; Values to add in script command: $2000, 97, $1


Notes:

1. The usability of Load Game function is not affected by whether the game has 'savegames on' state when you use this function or 'savegames off' state.

2. The purpose of the $2000, 70, $1F69 trigger is to play sound 105.
But why do we want to play that sound when a savegame has been loaded?
Well, the order of the triggers in a triggergroup sometimes matters - just as now: in TriggerGroup#9 the sound-playing trigger has to be before the popping-up trigger.
If you load with the usual system to a checkpoint then the game will load Lara to the popping-up point. And this point is after the playing trigger, so the game won't start the sound after it has loaded Lara to this point.
 
So that's why the game plays sound 105 with TriggerGroup#10 when a savegame has been loaded.

3. We have no problem that's similar to problem of the sound files with text 'CHECKPOINT' that's just on the screen when a savegame has been loaded.

4. In the example above, loading savegame1 will load to checkpoint#4. So if you want to go back to checkpoint#5 after this savegame1 loading - without journeying the route again from checkpoint#4 to checkpoint#5 - then you have to save the game in savegame2 (or any other) slot at checkpoint#5, before this savegame1 loading.

2.3. USING SECOND USUAL SAVING-LOADING METHOD

Technical note:

The unpreventable bugs of the method mostly exist because of two reasons:

- Game Backup type saving and loading always have the same rules just as the usual saving and loading system. - For example, if you use CUST_CD_SINGLE_PLAYBACK constant to adjust audio files for the usual system then this adjustment will also be valid for Game Backup type method.
So one of the major problems now is hard to tell the game in the triggers or in the script that if I'm just talking about a savegame-type saving-loading or a backup-type saving-loading.

- The another major problem is the things always have their saved values.
For example: you have a position (let's call it 'A'), and you save it. After that, Lara reaches another position (let's call it 'B') and does something there. You try to remind the game of Lara doing something in position B so you take Variable X there and give that variable the value 1 (saying 'value 1 means something happened here, but if nothing happened here then Variable X doesn't get value 1'). After that, you load position A, thinking 'I use Variable X as a condition. So if that thing happened, i.e. if Variable X=1 then I get the game to do Y thing at position A, but if that thing didn't happen, i.e. if Variable X not = 1 then I don't get the game to do Y thing at position A'. - But you gave Variable X value 1 
after you saved position A. And the game will load that non-1 (for example, 0) value if you load position A, so it's sure that you won't find Variable X=1 value at position A.

(No, GT_AFTER_RELOADING_VARIABLES, GT_BEFORE_SAVING_VARIABLES or any similar thing doesn't work in these cases.)
You may think: 'of course they have their saved values', but we would need the positions not to have some values to prevent some bugs of this method.

2.3.1. The way the things work

These things happen when you hit F5 to save:

1. The original task of F5 is forbidden, so when you hit F5 then the saving procedure starts - but not in the usual way.
2. The game saves Lara's position in a Backup slot. (Not in the Backup 1 slot, but, for example, Backup 2 slot.)
3. The game loads Lara to the checkpoint she reached last before this saving, i.e. to the position saved in Backup 1 slot. - The load camera picture of this level will cover the screen (automatically) all the time when this saving procedure are happening so the player will think Lara didn't lose her position during the procedure.
4. The game makes the savegame panel pop up.

Unpreventable bug#1:
When Lara reached that checkpoint, then maybe some audio and/or sound was playing. You'll hear that audio/sound when you are loaded here now. That's why you'll know that you're not at the position of Backup 2, i.e. Lara lost her position.
I mean I wanted to make the game become dumb while Backup 1 is loaded during the saving procedure but it didn't work properly.


Alternative endings of the method:

Ending#1:

5. Hit ENTER.
6. The game saves.
7. The game loads Lara back to the position of Backup 2.

Ending#2:

5. Hit CTRL.
6. The game saves.
7. The game leaves Lara at the position of Backup 1.

Ending#3:

5. Hit ESC.
6. The game leaves Lara at the position of Backup 1.

So, as a player, you'll think these things happened:

- Hitting ENTER: 'I saved the game'.
- Hitting CTRL: 'I saved the game and then the game loaded me to the checkpoint Lara reached last before this procedure'.
- Hitting ESC: 'I aborted the saving procedure and then the game loaded me to the checkpoint Lara reached last before this procedure'.

(The original task of hitting CTRL - saving the game - and hitting ESC - aborting the saving procedure - were changed automatically because of the circumstances of the setup of the second method. I couldn't prevent those automatic modifications without making bugs - but, hey, you can use ENTER and CTRL for not the same purposes now in the savegame panel.
The original task of hitting ENTER - saving the game - would also been changed if I hadn't done some things in script - see below.)

Unpreventable bug#2:
As you can see, the 'aborting the saving procedure' task is missing now.
So, if you start the saving procedure then you'll have to save in a savegame slot everyway, with hitting ENTER - even if you don't want to use that saved position later at all - or else Lara will lose her position at the end of the procedure.


2.3.2. Using Save Game function

You have to place this trigger in every level on the square where LARA object is placed:

; Exporting: TRIGGER(18:0) for FLIPEFFECT(51)
; <#> : Keyboard. Disable <&>keyboard command for (E) time
; <&> : Save the game (special)
; (E) : Forever (use other action/effect to disable it)
; Values to add in script command: $2000, 51, $12


You think this trigger means you're not allowed to save by the usual system in the whole level. But we use script (see below) to give F5 another method to save (see above), so this trigger means these two things now:

- when you hit F5 then the savegame panel won't pop up at once, but another saving procedure will happen, and
- you're not allowed to save by choosing Save Game in inventory. (Anyway, if you use that disabling trigger then you don't have Save Game in inventory.)

You have to use these rows in script, only in all [Level] blocks:

GlobalTrigger= 6, FGT_REMOVE_INPUT, GT_KEYBOARD_CODE, 63, IGNORE, 11, IGNORE
GlobalTrigger= 7, IGNORE, GT_SAVED_SAVEGAME, IGNORE, 14, 15, IGNORE
GlobalTrigger= 8, IGNORE, GT_LOADED_SAVEGAME, IGNORE, IGNORE, 16, IGNORE
GlobalTrigger= 9, IGNORE, GT_CONDITION_GROUP, IGNORE, 17, 18, IGNORE
Organizer= 2, FO_TICK_TIME, IGNORE, 0, 12, 2, 13
TriggerGroup= 11, $2000, 235, $48, $2000, 127, $2
TriggerGroup= 12, $2000, 97, $2
TriggerGroup= 13, $2000, 98, $1, $2000, 53, $12
TriggerGroup= 14, $8000, 28, $30C
TriggerGroup= 15, $2000, 98, $2
TriggerGroup= 16, $2000, 234, $48, $2000, 70, $1F69
TriggerGroup= 17, $8000, 72, $2C
TriggerGroup= 18, $2000, 128, $2

STARTING THE SAVING PROCEDURE

The GlobalTrigger#6 uses key F5 (see: keyboard code 63) as a condition. But thanks to FGT_REMOVE_INPUT constant, the disabled task of key F5 won't be enabled by that condition. So when you hit F5 then the savegame panel won't pop up but only the TriggerGroup#11 will happen.

; Exporting: TRIGGER(2:0) for FLIPEFFECT(127)
; <#> : Organizer. Enable <&>Organizer
; <&> : Organizer= 2
; (E) : 
; Values to add in script command: $2000, 127, $2


This trigger starts Organizer#2.

We need some time gap between saving Backup 2 and then loading Backup 1 to prevent some malfunction. That's why we use an organizer. We use FO_TICK_TIME constant, so the gap is small really, because this constant means that the unit of the organizer is not second but frame. (One second is thirty frames.)
One frame is too small for the proper working, so we use two frames as a gap in Organizer#2.

; Exporting: TRIGGER(2:0) for FLIPEFFECT(97)
; <#> : Backup. Save in silent way the current game in <&> backup file
; <&> : Game Backup 2
; (E) : 
; Values to add in script command: $2000, 97, $2


This trigger saves Lara's position in Game Backup 2 slot.

As we know, $2000, 98, $1 trigger loads Lara to the latest checkpoint, i.e. the actual position of Game Backup 1 slot.
And, as we know, $2000, 53, $12 trigger makes the savegame panel pop up.

(As I said before, the positions keep their saved values. Then how can we make the savegame panel pop up after loading Backup 1 slot? After all, Organizer#2 is started after saving in Backup 1, so when we loaded Backup 1 then Organizer#2 should be aborted without that popping-up, because Organizer#2 doesn't exist at Backup 1. - I don't know. I think this is a 'huge luck'. Maybe there are some triggers that are exceptions.)

FINISHING THE SAVING PROCEDURE

The GlobalTrigger#7 says if you've just saved the game and the condition in TriggerGroup#14 is also true then TriggerGroup#15 will happen.

; Exporting: TRIGGER(2:0) for FLIPEFFECT(98)
; <#> : Backup. Restore (load) the <&>backup file in (E)way
; <&> : Game Backup 2
; (E) : Standard way (progress bar + load camera screen)
; Values to add in script command: $2000, 98, $2


This trigger loads Lara back to the position of Game Backup 2 slot after saving the game.

But you want to load Lara there only if we saved with Save Game function and not with an automatic saving in a Backup slot. You have only one mode to 'explain to the game' that you're talking about only Save Game function now: you'll mention the key ENTER as a condition. Because you use ENTER at Save Game function, but don't use at Backup savings.

; Exporting: CONDITION(12:56) for PARAMETER(28)
; <#> : ENTER
; <&> : Keyboard. <#>keyboard scancode is currently (E)
; (E) : ACTIVE (Multi shot for positive condition)
; Values to add in script command: $8000, 28, $30C


This trigger says the condition is true if the player uses key ENTER in multi shot mode.
No, multi shot doesn't mean two or more hits. - I'll explain it:

1. The savegame panel is on the screen.
2. The player hits ENTER to save (Shot#1).
3. The panel closes.
4. The ENTER is still pushed for a short moment (Shot#2) before the player releases that.

The first shot is in the 'dead time' of the game (i.e. when it's frozen under the savegame panel), so the condition couldn't sense that (properly).

(So key ENTER could keep its original saving task because we defined that multi shot condition.)

THE PROBLEM OF THE ENDLESS ORGANIZER

The Organizer#2 is working now when the game saves Backup 2. So, when the game loads back Lara to Backup 2 at the end of the saving procedure, then Organizer#2 exists, that's why goes on: the game loads to Backup 1 again, and the savegame panel pops up again etc.
So you have to abort the Organizer#2 at the end of the procedure. So you'll take a variable and adjust its value 1 when the game has been loaded - i.e. at the end of the procedure, when the game loads back to Backup 2.
And you'll adjust the value 0 of this variable when the organizer starts. So, if you start organizer then the variable is 0, i.e. it's also 0 when it reaches the Backup 2-saving point after that. But, after the game has loaded back to this saving point at the end of the procedure then the variable takes value 1. - So we need a condition: if variable=1 then let the game abort the organizer.

This variable is Local Byte Delta1 now.

The GlobalTrigger#8 says if you've just loaded the game then TriggerGroup#16 will happen.

; Exporting: TRIGGER(72:0) for FLIPEFFECT(234)
; <#> : Variables. Numeric. Set in <&>Variable the (E)bit
; <&> : Local Byte Delta1
; (E) : Bit 0 ($00000001 ; 1)
; Values to add in script command: $2000, 234, $48


This trigger gives Local Byte Delta1 variable the bit 0 (value 1).

; Exporting: TRIGGER(72:0) for FLIPEFFECT(235)
; <#> : Variables. Numeric. Clear in <&>Variable the (E)bit
; <&> : Local Byte Delta1
; (E) : Bit 0 ($00000001 ; 1)
; Values to add in script command: $2000, 235, $48


And this trigger (in TriggerGroup#11, when the game starts Organizer#2) clears that value 1, giving the variable the value 0.

The GlobalTrigger#9 says if the condition in TriggerGroup#17 is true then TriggerGroup#18 will happen.

; Exporting: CONDITION(44:62) for PARAMETER(72)
; <#> : Local Byte Delta1
; <&> : Variables. The <#>Numeric Variable has the (E)Bit set
; (E) : Bit 0 ($00000001 ; 1)
; Values to add in script command: $8000, 72, $2C


This trigger examines if Local Byte Delta1 is 1.

; Exporting: TRIGGER(2:0) for FLIPEFFECT(128)
; <#> : Organizer. Stop <&>Organizer
; <&> : Organizer= 2
; (E) : 
; Values to add in script command: $2000, 128, $2


This trigger aborts that endless organizer.

Notes:

1. The game gives this variable the value 1 after every type loading (so also at the points where we don't want to), but it doesn't disturb the working of the organizer, because the start of the organizer always gives value 0 to the variable.

Look at that strange point again where the savegame panel pops up: when the game has loaded Backup 1 then the game - ignoring the condition (maybe because there's 'dead time' there) - won't give value 1 to the variable.
It's another 'huge luck' there, because value 1 would break organizer at that point - and we don't want that breaking, of course.

2. Unpreventable bug#3:
You have a similar problem with sound 105 just as at the first method: you can hear that sound when Lara reaches a checkpoint, but you can't if she gets back to a checkpoint with some loading.
I think you have only two (not really proper) solutions for that problem:

- You don't care about the problem, so you won't hear sound 105 when Lara has been loaded back to a checkpoint.
- You use that sound 105-playing trigger ($2000, 70, $1F69 - see above in TriggerGroup#16) also with that loading condition. But it means you'll hear that sound after every loading - i.e. also when the game loads Lara back to the position of Backup 2 (that is not at a checkpoint) at the end of the saving procedure.

(Of course, it's also a solution if you don't use Sound triggers to the checkpoints at all. In this case you'll prevent this annoying bug - but skip the sound feature.)


2.3.3. Using Load Game function

When the player loads a savegame slot then the game will load Lara in a position of a checkpoint - not the latest checkpoint under any circumstances but the one where the saving in this savegame slot happened:

L1: this is a point anywhere in the level. The player starts saving procedure here. During the procedure the game loads Lara to the latest checkpoint (checkpoint#4) and saves her position there in savegame1 slot in the savegame panel. (Or any other savegame slot. It doesn't matter ever, actually.)
P: this is a point anywhere in the level. If the player hits C here then the game will load Lara to the position of Game Backup 1 that is saved at the latest checkpoint: checkpoint#5. If the player loads savegame1 slot here from the savegame panel then the game will load Lara to the checkpoint that is saved in savegame1 slot: checkpoint#4.
(P can also mean title when you got back there after Lara left C5. Or another level that Lara reached after she left C5.)

But you have the same problem just as at the first method: when you load a savegame slot and it's not the latest checkpoint then - it's logical, though - it doesn't become the latest checkpoint automatically.
You need these rows in all [Level] blocks to prevent that problem:

GlobalTrigger= 10, IGNORE, GT_KEYBOARD_CODE, 64, IGNORE, 19, IGNORE
GlobalTrigger= 11, IGNORE, GT_KEYBOARD_CODE, 28, 20, 21, IGNORE
GlobalTrigger= 12, IGNORE, GT_KEYBOARD_CODE, 1, IGNORE, 22, IGNORE
TriggerGroup= 19, $2000, 234, $8
TriggerGroup= 20, $8000, 8, $2C
TriggerGroup= 21, $2000, 97, $1
TriggerGroup= 22, $2000, 235, $8

You'll mention key ENTER again to explain to the game that you're just using Load Game type loading (when you use ENTER) and not some other type loading (when you don't use ENTER). (If you don't do that then the position of Backup 2 would also become the latest checkpoint when the game loads that at the end of the saving procedure. - No, it doesn't matter now you hit ENTER to save just before that loading.)
But how will the game know that you use ENTER at Load Game and not at another function? - To solve that problem, you'll use a variable. It will get value 1 if you hit F6 to load a savegame. And you'll use a condition: the game will care about that 'ENTER-mention' only if the variable=1. (GT_LOADED_SAVEGAME condition with multi shot ENTER wouldn't work now.)

The GlobalTrigger#10 says if the player hits key F6 (see: keyboard code 64) then TriggerGroup#19 will happen.

; Exporting: TRIGGER(8:0) for FLIPEFFECT(234)
; <#> : Variables. Numeric. Set in <&>Variable the (E)bit
; <&> : Global Byte Delta1
; (E) : Bit 0 ($00000001 ; 1)
; Values to add in script command: $2000, 234, $8


This trigger gives Global Byte Delta1 variable the bit 0 (value 1).

The GlobalTrigger#11 says if the player hits key ENTER (see: keyboard code 28) and the condition in TriggerGroup#20 is also true then TriggerGroup#21 will happen.

; Exporting: CONDITION(44:62) for PARAMETER(8)
; <#> : Global Byte Delta1
; <&> : Variables. The <#>Numeric Variable has the (E)Bit set
; (E) : Bit 0 ($00000001 ; 1)
; Values to add in script command: $8000, 8, $2C


This trigger examines if Global Byte Delta1 is 1.

And $2000, 97, $1 trigger saves the game in Game Backup 1 slot, saying: 'if you hit ENTER after you hit F6, then the checkpoint that has just been loaded will be the latest checkpoint'.

Notes:

1. You have to clear value 1 from that variable after this loading procedure or else it will be problematic (i.e. the game will always save the actual position in Backup 1 slot), if you hit ENTER later at any function of the game:

- The value got 1 only when you hit F6, so the value is 0 at the position that you will load now. So, if you load that position then the game will clear value 1 automatically.

- If you don't hit ENTER, i.e. if you abort the procedure with hitting ESC then the game will clear value 1 by GlobalTrigger#12:
The GlobalTrigger#12 says if the player hits key ESC (see: keyboard code 1) then TriggerGroup#22 will happen.

; Exporting: TRIGGER(8:0) for FLIPEFFECT(235)
; <#> : Variables. Numeric. Clear in <&>Variable the (E)bit
; <&> : Global Byte Delta1
; (E) : Bit 0 ($00000001 ; 1)
; Values to add in script command: $2000, 235, $8


This trigger clears value 1 of Global Byte Delta1.

(This clearing works at every ESC-hitting. But it doesn't matter because Global Byte Delta1 has nothing to do with other game procedures that need ESC.)

2. Maybe you are in Level A when you load a savegame that was saved in Level B.
That's why I chose a 'global' type variable: you'll define that value 1 in Level A, but the game will take the value 1 of the variable from A to B, to define that saved position in Level B as the latest checkpoint.
Though, it's strange: when you hit ENTER this is the ENTER of GlobalTrigger#10 of Level A (because we're not talking about multi shot now), but when $2000, 97, $1 trigger saves the game then that $2000, 97, $1 is the $2000, 97, $1 of Globaltrigger#11 of Level B. - So one half of the procedure happens in Level A, and the other half happens in Level B. (Or just I think that's what happens. - Whatever: the setup seems to work and that's the point.)

3. Unpreventable bug#4:
This problem-preventing won't work, if you load a savegame from title. - So, if you choose a savegame slot in the Load Game menu of title, then the game will load that position but it won't become the latest checkpoint after it's loaded if it's not the latest checkpoint.
Of course, this is not a big problem: after all, as I said, if Lara steps off the square of that checkpoint after it's loaded and steps on that again at once then she will activate that placed Backup 1-saving trigger again, correcting the problem. But it's lame.
(Anyway, naturally you can also define a new latest checkpoint if you load a savegame from this/another level or if you make Lara walk/run/swim etc. to another checkpoint.)

But be careful: this bug will get the things pretty messy if the player forgets about the recovering. (For example, don't forget: saving with F5 always loads Lara back to the latest checkpoint - even if it's just the wrong checkpoint.)


4. Using this saving-loading method use only the 'classic' savegame panel. If you use (see SavegamePanel script command) the new one that may generate a bug. (I.e. the new panel breaks the effects of GlobalTrigger#10-12.)

5. In the example above, loading savegame1 will load to checkpoint#4. So if you want to go back to checkpoint#5 after this savegame1 loading - without journeying the route again from checkpoint#4 to checkpoint#5 - then you have to save the game in savegame2 (or any other) slot after Lara reached checkpoint#5, before this savegame1 loading.