A few weeks ago I published a new youtube video where I presented the debugging interface of Time Commando.
Maybe it is time to look in more details about some of the elements I presented?
In the video I mentionned the ACF and HQR files.
Let see what some of these HQR scene level files are made of.
SCENEPC.LSTAs anyone who dug into Little Big Adventure knows, most of the data in the game is stored in the HQR archive files, generated by MAKEHQR from a set of files and associated LST file.
Here is an example of LST file generated for the PC version of Time Commando levels:
Knowing the content of this file is specially important if you plan to reverse engineer the game by extracting the data, because it tells you the type of each entry, and in which order they are stored.
In this particular article I will just concentrate on the first one, the SCENE.SCC file, which is binary exported version of the SCENE.SCE file.
SCENE.SCEThe "SCE" stands for "SCEne".
Each of the levels has a single SCE file with the list of all actors, weapons, traps, interactive zones, etc... which will get processed by the Time Commando Script Compiler into a set of binary files designed to be directly loaded into memory in an immediately usable format requiring no additional memory allocation.
The typical SCE file content looks like what follows.
Most of the system uses a structure syntax with sections starting by "Set" and ending by "End".
Many of these sections have "Life Frames" which define when the element is enabled and disabled, to avoid having to process all of them during the entire duration of the level.
SetObj [Object Name] [Class name]
InitPosition [X] [Y] [Z]
InitLifeFrame [Start] [End] [Lock]
InitBody [3D Object]
InitLifePoint [Hit points]
InitWeapon [Weapon name] [Ammonition count]
(one or many subroutines)
The "Obj" sections are used to define complex entities which have a visual representation, such as the hero, the enemies, traps, container where you empty the memory chips, etc.... the first parameter after the SetObj is the individual name of this object, and the second parameter defines it's "class", which is defined in some other file1 and tells us which meshes and animations are available2.
The various Init parameters define the starting position, orientation, mesh and animation used by this particular actor.
The "Lock" frame, is a parameter used to control the movement of the camera: As long as the object is active (alive), the camera path will not move farther than the indicated frame id.
There are also optional "InitFlag" commands which can be used to turn of some of the object flags:
- InitFlagNoZv will deactivate the object bounding box
- InitFlagNoTarget makes this object impossible to target
- InitFlagNoGravity stops the object from falling to the ground
- InitFlagNoShadow disables the object shadow casting
- InitFlagNoGiveWeapon makes the weapon disappear on death
The "Track" sections are somewhat similar, but have no attached visual representation. We used that to run some stand-alone scripts that run on the side to control events
InitLifeFrame -1, 175
Similarly, the "Weapon" sections are used to put some weapons on the floor for the player to find.
In this example, the -1 value in InitPosition means that the object will be snapped to the ground level automatically.
SCENE.SCCThe "SCC" stands for "Scène Compilée" or "Compiled Scene" in English.
This file starts by a SCENE structure which contains the following:
U8 base_sprite_2d // PSX Only
U8 base_sprite_3d // PSX Only
The num_stage and num_run fields are just a basic protection to make sure that people can't just change the order of levels, like starting directly by the level 7 by copying the folder3.
The "nb_objet" field tells us how many OBJECT structures follow:
Here are the possible values for the "flag" field:
#define OBJECT_NO_FLAG 0
#define OBJECT_TARGET 1
#define OBJECT_SHADOW 2
#define OBJECT_ZV 4
#define OBJECT_IMMORTEL 8
#define OBJECT_GRAVITY 16
#define OBJECT_INSHOCKABLE 32
#define OBJECT_GIVE_WEAPON 64
#define OBJECT_FLYING 128
#define OBJECT_HERO_RUN 256
the count_track field tells us how many TRACK structures follow:
and then finally the count_weapon field tells us how many WEAPON structures follow:
I'm not 100% certain what nb_arme and num_arme are, one is probably the weapon type and the other how many are there, but not idea which is which!
The various offset_ variables are the relative positions of the resources in the relevant HQR file, so for example offset_track would point to the track data (script program) in the TRK entry in the HQR file.
The Red Dragon mysteryA few days ago, I got some comments from RK.Walnut on the blog, with a question regarding the fight with the Japanese Red Dragon:
Ok, so here is a picture of what he is talking about:
Here is the entire unmodified script for the dragon4.
So, what can we learn from this script?
From the initialization parameters, we know that the Dragon has 40 life points and four sections in his life bar (InitLifePoint 40,4), and is equipped from 8 fireballs (BouleDeFeu) as well as 199 spits (Crache) attacks, and that upon death the player will not receive any of these (InitFlagNoGiveWeapon).
Then, we enter the main program which will loop as long as Stanley is still alive.
The code stores the distance between Stanley and the Dragon into the "dho" (Distance HerO) variable and immediately after checks the value of the "jouefurie" (Play fury) variable.
If the fury variable is true we wait for the end of any choc animation (means the hero managed to hit the dragon and it's currently playing that) before unleashing the "Headbutt" (CoupBoule) animation.
Else we enter a long list of checks:
- Have we been hit?
- Was it twice with the same attack? Then set the fury variable to true
- Else play an animation from a set defined by the difficulty level
- Or maybe we were blocked? Then we try to move to the center of the room
Based on the distance the Dragon will try to move forward, backward, use specificaly tailored attacks depend of how far to reach.
Additionally, the SiEsquive sub routine is used to check if the Dragon actually managed to hit, and if it failed twice in a row will switch to defensive mode using his guard animations.
You may have noticed the "& !GetRandom(_dif)" on the right and side of each of the distance checks? It's simply a way to add some unpredictability by using the random generator modulo the difficulty level:
In easy mode the result will always be the same and the proper distance check will always be selected, while in medium difficulty this will happen half of the time and in hard one third of a time.
Originally, we had a combat module written entirely in C, with a bunch of parameters to define the behavior, which animations to use in which context, etc... but it quickly became a bottleneck for the development because this thing would have had to be used for every single entity of the game, and having to handle things ranging from humanoids to flying sharks, raging bulls to dragons, was definitely not going to cut it, so instead we decided to extend the scripting system and to manually implement every single character5
I will let future generations to decide it this whole thing made sense or was completely bonkers :D
What's next?I still have quite some material to share, if only because I've still not explained how the scripting system was implemented, and how the video decoder worked.
I do not have a planned schedule for that, I'm doing plenty of other stuff on the side, but don't worry, more thing will come, you just need to be patient.
I hope this was interesting :)