Room Editing
As seems to be the case with most of these tutorials, many thanks must go to Mario "HCl" Brito, who did most of the original research into the subject.
ROM files
We will first deal with the ROM files. It is recommended that you start off by decoding an existing ROM file in WCPPas - writing the entire file from scratch would be utterly pointless.
The structure of a ROM file is (thank goodness) relatively simple. In brief, it looks like this:
{
FORM "MED_"
{
CHUNK "VERS"
CHUNK "BACK"
FORM "OBJS"
{
FORM "OBJ_" // There is usually several
of these forms
{
CHUNK "COMN"
FORM "PLST"
{
FORM "PROP" // There is usually around
ten of these; not all are fully understood.
}
}
}
}
FORM "EDTR"
{
CHUNK "CRC"
FORM "OBJS" // This one seems to usually
be completely empty
}
}
Of the above, it is the OBJ_ forms that will interest us the most. The BACK chunk will also need to be edited. Finally, the EDTR form should probably be edited, but this may not be completely necessary - as far as I have seen, changes to the EDTR form seem to have no effect.
We start with the BACK chunk. This chunk only
tells the game what file to use as the background image, so it is very
simple. It looks like this:
CHUNK "BACK"
{
cstring "rec800.bmp"
}
In the above case, the game uses the file rec800 as the background. Note that, contrary to what the above might indicate, the game does not use a BMP file. Rather, it uses an IFF file (which, in this particular case, is nothing more than a MAT file with a changed extension). So, "rec800.bmp" is in fact \gameflow\rec800.iff. Any MAT file can be used as the background (except possibly animated MATs - I have never tried those, and don't know if they will work properly). The size of the image does not matter, though obviously it is best to use an image which covers the entire screen.
The OBJ_ forms
The second thing is the OBJ_ forms inside the OBJS form (inside MED). Each of these forms defines a single "hotspot". A hotspot is a part of the room which behaves differently than the rest of the room. It can react to being clicked upon (for example, displaying the killboard), it can react to the mouse moving over it and away from it (for example, an animated door which opens when the player moves his mouse over it), or it can simply be an animation which continually cycles regardless of what the player does. Of course, it can also combine any of the above options.
The first thing to note is the COMN chunk. This
chunk contains a number of floats and long integers. For example, it
might look like this:
CHUNK "COMN"
{
float 0.2
long 6
float 0.0 // X
float 0.5 // Y
float 0.25 // X
float 0.48 // Y
float 0.21 // X
float 0.57 // Y
float 0.38 // X
float 0.55 // Y
float 0.29 // X
float 0.81 // Y
float 0.0 // X
float 0.81 // Y
}
The first float ("0.2") is the ID of
the hotspot. From what I've seen these are generally irrelevant. In
fact, cerberus.rom, the original SO room file, has 0.0 entered for most
of its hotspots. Generally, however, it is worthwhile to enter a different
number for each, so that you can easily recognise which hotspot you're
editing.
The second number is a long. This number defines the number of coordinate
pairs that the hotspot uses to define its area on the screen. You can
have as many coordinate pairs as you like (well, there's probably a
limit, but I haven't found it), so this number can be as high as you
like too - but you must make sure to have the right number of coordinate
pairs.
The rest of the chunk is taken up by the coordinate pairs themselves.
Each pair defines a single corner of the hotspot, and consists of an
X and a Y coordinate. The coordinate pairs should be defined in a clockwise
order (for example, the top left corner will be followed by the top
right corner, then the bottom right corner, and finally the bottom left).
Note that the coordinates are floats, ranging in value from 0.0 to 1.0.
These are basically percentages of the screen. So, in a 640x480 resolution,
0.5 on the X axis would mean pixel 320. This is why, if you try to use
the original SO room files on a higher resolution without using a scaled-up
background file, the hotspots will not be properly aligned with the
graphics.
You can of course have a hotspot which takes up the entire screen, though
this would be rather pointless ;). I do not know how a game would react
to having multiple hotspots taking up the same part of the screen. It
probably would not crash, but we can only guess what would happen in
the case of two clickable hotspots in the same place.
The next part is the PLST form, with its PROP forms. Each PROP form contains a single property chunk. We know of ten different property chunks that can be included, and not all of them are always present inside a particular PLST form. The order in which these are placed inside the PLST form probably does not matter. We'll go over all ten of these, one by one:
FORM "PROP"
{
CHUNK $56A0F92F
{
long 15
}
}
This is a total unknown. However, the long seems to always be "15", so there seems to be no reason to edit it.
FORM "PROP"
{
CHUNK $DC7798E0
{
long 27
}
}
This chunk contains the number of the line which will be displayed when the mouse hovers over it. The lines are drawn from the file \language\gameflow.eng. For hotspots which do not display any text, 0 should be used.
FORM "PROP"
{
CHUNK $69DEC1DB
{
cstring "G_RunSpecialSim"
long 0
long 0
}
}
The $69DEC1DB chunk contains the name of the function from the series file which will be activated when the player clicks on the hotspot (in this case, G_RunSpecialSim). Note that if a function with this name is not found, the game will crash when the room is loaded. It is possible that the game will also check if the function is present inside the mission file. I have not tried this, but WCP has several mission-specific room files whose functions seem to only be present inside the mission files. The remaining two longs are always set to "0". It is possible that these are not needed at all. If you do not want the hotspot to have such a function, use "NULL" as the string name.
FORM "PROP"
{
CHUNK $D6CCB441
{
cstring "NULL"
long 0
long 0
long 0
long 0
long 0
long 0
}
}
This is another total unknown. Judging from the structure, it is probably a reference to a function. How this function would be activated, however, is unknown. As far as I've seen, WCP/SO rooms all have "NULL" here too. As before, the "0" longs seem irrelevant.
FORM "PROP"
{
CHUNK $B5CD23F4
{
cstring "G_Simulator_Fidget"
long 0
long 0
long 0
long 0
long 0
long 0
}
}
The $B5CD23F4 chunk contains the reference to the function which is activated when the player moves his mouse away from the hotspot. Like with $69DEC1DB, the game will crash if it doesn't find the function, but "NULL" can be used if no function is needed here. Again, the "0" longs seem irrelevant.
FORM "PROP"
{
CHUNK $29F0B42F
{
cstring "G_Simulator_Hover"
long 0
long 0
long 0
long 0
long 0
long 0
}
}
Essentially, this chunk is identical to the above chunk, except that
it defines the function to be activated when the player moves the mouse
over the hotspot.
FORM "PROP"
{
CHUNK $554D814C
{
long 1
}
}
This chunk seems to determine what shape the
mouse cursor takes when it is moved over the hotspot. The chunk is optional
- if it is not there, the game
assumes that its value is set to "0", which means that the
cursor will turn from the arrow into the targetter cursor. On the other
hand, setting this
value to "1" will result in the cursor not changing shape
at all (good for
hidden hotspots). It may be that other cursor types can be triggered
by
using different numbers here, but nobody has experimented with that.
FORM "PROP"
{
CHUNK $674024FE
{
cstring "shot800"
}
}
This chunk is similar to the BACK chunk at the start of the file - it tells the game what graphics file is to be placed on top of this hotspot (the file does not have to conform to the dimensions of the hotspot, though this is obviously desirable in the case of clickable hotspots). Note that this is usually not needed at all (in which case, you enter "NULL" as the string) unless the hotspot is to use animated graphics. The file pointed to is an IFF file contained inside the \gameflow folder, and can contain several animation frames (we will deal with this later).
FORM "PROP"
{
CHUNK $54433211
{
long 0
long 7
long 0
long 0
long 0
long 0
}
}
This chunk is only needed if the $674024FE chunk specified a graphics file to use. The chunk defines the manner in which the graphics file is to be animated. If you don't want the hotspot to animate at all, simply use 0 for all the longs. Otherwise:
The first long defines the number of frames used by the animation which is activated when the mouse moves away from the hotspot - the animation starts from the frame number defined here, and goes down to frame 0.
The second long defines the loop which is active at any time when the mouse is not over the hotspot. Note that if the mouse moves over it, the loop is immediately broken - frame 0 is displayed and the animation stops - unless the hotspot has a mouse-over loop defined too. Note that this sort of loop is designed to be used _instead_ of the mouse-move-away loop, not with it.
The third long is tied to the first two longs
- while they define the range of their relevant animation, this one
defines how the animation itself works. The value entered here will
have different effects depending on which of the two animations defined
above you use.
If you use the mouse-move-away animation, entering 0 here simply returns
the hotspot to frame 0 without any animation when the mouse moves away
from it. For the whenever-mouse-is-away animation, 0 will make it a
straight loop from frame 0 to the end of the animation, and then re-starting
the loop from frame 0 again.
A 1 has the same effect on both - it makes the animation work whenever
the mouse is away from the hotspot - the animation starts from 0, goes
on to the last frame, briefly pauses without displaying _any_ frame
(ie., the background is visible - this should only be used when the
animation is used to move the background, and not, for example, a human
figure imposed on top of the background), and then animates from the
last frame down to frame 0, followed by another pause and a repeat of
the cycle.
A 2, if you are using the mouse-move-away animation, will trigger an
animation from whatever frame is being displayed down to frame 0 (so,
if the animation is already at frame 0, this will appear to do nothing).
If you are using the whenever-mouse-is-away animation, on the other
hand, the effect will be that the animation will only be run once, from
frame 0 to the end. After it's over, the animation will disappear altogether,
leaving the background visible. If the player moves the mouse over the
hotspot and then away again, the cycle will be triggered again.
Finally, anything higher than a 2 has the same effect on both - it causes
the game to pause the animation (but keep it visible) whenever they
are triggered. So, if you have a mouse-over animation, and then move
the mouse away, the animation will "freeze" until you trigger
the mouse-over animation again.
The fourth, fifth, and sixth longs seem to work approximately the same as the first three - the fourth is an animation from the end frame to frame 0, the fifth starts from frame 0 and goes on to the end frame, and the sixth determines how they work. The difference between them and the first three longs is that while they worked either when the mouse was moving away from the hotspot or was already away from it, these do the opposite - the fourth works when the mouse moves over the hotspot, and the fifth works whenever the mouse is over the hotspot.
Usually, for something like a door, you will want a combination of the first and fifth longs. For a loop which works constantly even when the hotspot reacts to the mouse moving over or away from it, the second and fifth should be combined. The second can also be used by itself for situations where you want the loop to stop when the mouse moves over the hotspot, or for a constant loop when the hotspot doesn't react to the mouse (see the next property chunk). Finally, the fourth generally doesn't need to be used at all.
FORM "PROP"
{
CHUNK $59738A81
{
long 4
long 1
long 235
long 198
}
}
If there is no graphics file defined for the
hotspot, the game ignores all but the second line of this chunk.
The first long defines the speed of the animation - the higher the number,
the faster the frame changing. The second long defines whether the hotspot
is active (1 is true, 0 is false). An inactive hotspot does not react
to mouse movement at all - it does not trigger a subtitle or a sound
when the mouse moves over it, and it never breaks off from the non-mouse-over
animation loop (if it has one). If does, however, call functions if
there are any specified, thus allowing you to create a hidden but clickable
hotspot (like the easter egg in SO).
Finally, the third and fourth longs define the X and Y coordinates of
the top left corner of the hotspot's graphics. These seem to be measured
in pixels. It is possible (but has not been tried) that floating point
values could be used instead, thus placing the corner at a percentage
of the screen rather than a fixed pixel.
The EDTR form
This form is a bit strange, in that it doesn't seem to do anything. Nonetheless, as with other things we don't understand, it's worthwhile to imitate SO just to avoid trouble. One possibility is that this form was only used by Origin's WCP editor. In this case, it would indeed be utterly useless for us, but who knows?
The form consists of two things - the chunk CRCS, and the OBJS form. It might, for example, look like this:
FORM "EDTR"
{
CHUNK "CRCS"
{
long 3
float 0.1
float 0.2
float 0.3
cstring "ToReadyRoom"
cstring "Simulator"
cstring "HotSpot"
}
FORM "OBJS"
{
}
}
From what I've seen, the OBJS form is always
empty. The information stored in the CRCS chunk is also very simple
- first, there is the number of ID entries to be included, then there
are the ID entries themselves. There seems to be one for every OBJ_
form inside the MED form's OBJS form. They are identical to the ID entered
inside OBJ_'s COMN chunk.
The last three lines in the above example are text labels for each of
the IDs. These do not seem to be related to the strings from \language\gameflow.eng
in any way.
This concludes the ROM file. Before we finish, however, we need to take a brief look at the IFF files.
The IFF files
As was mentioned earlier, these files are basically MATs. The background image, in fact, is exactly identical to a standard MAT file, except for the extension.
However, for IFF files containing several frames
of an animation, things get slightly more complicated. When you convert
the frames into the MAT format, each is a separate MAT file. So, first
you need to put them together into a single IFF file. WCPPas comes in
very useful at this point. Open a new file, and create a structure resembling
this one:
IFF "holo640.iff"
{
FORM "BITM"
{
file "c:\mydocu~1\ue\cic1\HoloANI_1_64.mat"
file "c:\mydocu~1\ue\cic1\HoloANI_2_64.mat"
file "c:\mydocu~1\ue\cic1\HoloANI_3_64.mat"
file "c:\mydocu~1\ue\cic1\HoloANI_4_64.mat"
file "c:\mydocu~1\ue\cic1\HoloANI_5_64.mat"
file "c:\mydocu~1\ue\cic1\HoloANI_6_64.mat"
file "c:\mydocu~1\ue\cic1\HoloANI_7_64.mat"
file "c:\mydocu~1\ue\cic1\HoloANI_8_64.mat"
file "c:\mydocu~1\ue\cic1\HoloANI_9_64.mat"
file "c:\mydocu~1\ue\cic1\HoloANI_10_64.mat"
}
}
The folders and names of the files listed above will of course be different depending on what it is you're putting together. Basically, however, you need to enter every frame as a "file" entry in the structure above.
Then you build the IFF file. However, the file is still not ready, because putting the files together like this has created a bug.
If you were to decode a standard MAT, you would
see the following structure:
{
FORM "BITM"
{
FORM "FRAM"
{
// Here is the main part of the MAT file
}
}
}
The structure of course is a bit more complicated, but fortunately we do not need to examine the insides of the FRAM form here. What is important here is that each MAT is contained inside a FRAM form, which in turn is contained inside a BITM form. Our IFF structure above, however, also included a BITM form. So, when we put the MAT files together into a single IFF, we ended up with a bunch of BITM forms inside a BITM form, which the game doesn't accept. What we want instead, is a single BITM form containing a FRAM form for every frame of the animation. In order to fix this problem, you should reduce WCPPas' MaxDecodeSize to something like 512. Now decode the IFF file that you had just created, and remove all the BITM forms except the very first one. Note that for every BITM form, you need to remove both the starting and the ending bracket. Now build the new IFF file, and it's finished.
Final notes about the animation files
It is worth noting that the animation IFFs, like any other MAT, support transparency - colour 0 will be transparent.
An interesting question would be what happens if, instead of an IFF file containing several frames, one was to use an animated MAT (which only contains references to frames stored as separate files). I have not tried this, so I do not know. Considering, however, that all the WCP and SO rooms used IFFs containing multiple frames instead of animated MATs, it is probable that doing so would cause problems at the least, and possibly even crashes.
Tutorial by Quarto